Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6290,6 +6290,10 @@ "lambda expression in an unevaluated operand">; def err_lambda_in_constant_expression : Error< "a lambda expression may not appear inside of a constant expression">; + def err_lambda_in_template_argument : Error< + "a lambda expression may not appear inside of a template argument">; + def err_lambda_in_function_signature : Error< + "a lambda expression may not appear inside of a function signature">; def err_lambda_return_init_list : Error< "cannot deduce lambda return type from initializer list">; def err_lambda_capture_default_arg : Error< Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -1423,9 +1423,15 @@ MaybeTypeCast, IsTypeCast }; - + enum class ConstantEvaluationContext { + Default, + InTemplateArgument, + InFunctionSignature + }; ExprResult ParseExpression(TypeCastState isTypeCast = NotTypeCast); - ExprResult ParseConstantExpression(TypeCastState isTypeCast = NotTypeCast); + ExprResult ParseConstantExpression( + TypeCastState isTypeCast = NotTypeCast, + ConstantEvaluationContext Ctx = ConstantEvaluationContext::Default); ExprResult ParseConstraintExpression(); // Expr that doesn't include commas. ExprResult ParseAssignmentExpression(TypeCastState isTypeCast = NotTypeCast); Index: include/clang/Sema/DeclSpec.h =================================================================== --- include/clang/Sema/DeclSpec.h +++ include/clang/Sema/DeclSpec.h @@ -1746,6 +1746,8 @@ /// DeclTypeInfo.back() will be the least closely bound. SmallVector DeclTypeInfo; + Declarator *ParentDeclaratorOfParameterDeclarator = nullptr; + /// InvalidType - Set by Sema::GetTypeForDeclarator(). unsigned InvalidType : 1; @@ -1798,12 +1800,13 @@ /// \brief If provided, the source location of the ellipsis used to describe /// this declarator as a parameter pack. SourceLocation EllipsisLoc; - + llvm::Optional PrecedingTok; friend struct DeclaratorChunk; public: - Declarator(const DeclSpec &ds, TheContext C) + Declarator(const DeclSpec &ds, TheContext C, Declarator *FunD = nullptr) : DS(ds), Range(ds.getSourceRange()), Context(C), + ParentDeclaratorOfParameterDeclarator(FunD), InvalidType(DS.getTypeSpecType() == DeclSpec::TST_error), GroupingParens(false), FunctionDefinition(FDK_Declaration), Redeclaration(false), Extension(false), ObjCIvar(false), @@ -1817,6 +1820,16 @@ /// declared with. const DeclSpec &getDeclSpec() const { return DS; } + const Declarator *getParentDeclarator() const { + return ParentDeclaratorOfParameterDeclarator; + } + void setFunctionPtrOperatorToken(tok::TokenKind Kind) { + PrecedingTok = Kind; + } + + bool isFunctionPtrOrRef() const { + return PrecedingTok.hasValue(); + } /// getMutableDeclSpec - Return a non-const version of the DeclSpec. This /// should be used with extreme care: declspecs can often be shared between /// multiple declarators, so mutating the DeclSpec affects all of the Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -829,6 +829,18 @@ /// cases in a switch statement). ConstantEvaluated, + /// \brief Like ConstantEvaluated above, except that the compile-time + /// evaluation is occuring within a template argument context. This + /// distinction is useful because certain expressions (lambda expressions, + /// even immediately invoked ones) are forbidden in such contexts. + ConstantEvaluatedInTemplateArgument, + + /// \brief Like ConstantEvaluated above, except that the compile-time + /// evaluation is occuring within a function signature. This + /// distinction is useful because certain expressions (lambda expressions, + /// even immediately invoked ones) are forbidden in such contexts. + ConstantEvaluatedInFunctionSignature, + /// \brief The current expression is potentially evaluated at run time, /// which means that code may be generated to evaluate the value of the /// expression at run time. @@ -909,6 +921,17 @@ return Context == Unevaluated || Context == UnevaluatedAbstract || Context == UnevaluatedList; } + bool isConstantEvaluated() const { + switch (Context) { + case ConstantEvaluated: + case ConstantEvaluatedInTemplateArgument: + case ConstantEvaluatedInFunctionSignature: + return true; + default: + return false; + } + llvm_unreachable("all control flow handled within switch"); + } }; /// A stack of expression evaluation contexts. Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -5051,7 +5051,6 @@ // '&&' -> rvalue reference SourceLocation Loc = ConsumeToken(); // Eat the *, ^, & or &&. D.SetRangeEnd(Loc); - if (Kind == tok::star || Kind == tok::caret) { // Is a pointer. DeclSpec DS(AttrFactory); @@ -5589,6 +5588,7 @@ bool hadGroupingParens = D.hasGroupingParens(); D.setGroupingParens(true); + auto TokKind = Tok.getKind(); ParseDeclaratorInternal(D, &Parser::ParseDirectDeclarator); // Match the ')'. T.consumeClose(); @@ -5601,7 +5601,11 @@ // An ellipsis cannot be placed outside parentheses. if (EllipsisLoc.isValid()) DiagnoseMisplacedEllipsisInDeclarator(EllipsisLoc, D); - + + if (!D.isFunctionDeclarator() && + isPtrOperatorToken(TokKind, getLangOpts(), D.getContext())) { + D.setFunctionPtrOperatorToken(TokKind); + } return; } @@ -6000,10 +6004,11 @@ // Parse the declarator. This is "PrototypeContext" or // "LambdaExprParameterContext", because we must accept either // 'declarator' or 'abstract-declarator' here. - Declarator ParmDeclarator(DS, - D.getContext() == Declarator::LambdaExprContext ? - Declarator::LambdaExprParameterContext : - Declarator::PrototypeContext); + Declarator ParmDeclarator(DS, + D.getContext() == Declarator::LambdaExprContext + ? Declarator::LambdaExprParameterContext + : Declarator::PrototypeContext, + &D); ParseDeclarator(ParmDeclarator); // Parse GNU attributes, if present. @@ -6216,7 +6221,41 @@ // Parse the constant-expression or assignment-expression now (depending // on dialect). if (getLangOpts().CPlusPlus) { - NumElements = ParseConstantExpression(); + // TODO: The lambda in an alias/typedef/template-argument needs its own + // error message. + const bool IsWithinFunctionSignature = [&D] { + const Declarator *ParentDeclarator = &D; + // TODO: Should we check lambda parameters too? + const bool IsPrototypeContext = D.getContext() == D.PrototypeContext; + if (IsPrototypeContext) { + // Climb up all parameters (even those that are parameters of function + // ptr parameters) to the parent function declarator. + while (auto *PD = ParentDeclarator->getParentDeclarator()) + ParentDeclarator = PD; + } + //TODO: Merge these into one conditional? + // No lambda expressions permitted in a typedef + if (ParentDeclarator->getDeclSpec().getStorageClassSpec() == + DeclSpec::SCS_typedef) + return true; + // ... or in a type alias + if (ParentDeclarator->getContext() == Declarator::AliasDeclContext || + ParentDeclarator->getContext() == Declarator::AliasTemplateContext) + return true; + // ... or in a template type argument context + if (ParentDeclarator->getContext() == Declarator::TemplateTypeArgContext) + return true; + // ... or in the return type of a function declarator + if (ParentDeclarator->isFunctionDeclarator()) + return true; + // ... or in the parameters of a function. + return IsPrototypeContext && !ParentDeclarator->isFunctionPtrOrRef(); + }(); + NumElements = ParseConstantExpression( + NotTypeCast, + IsWithinFunctionSignature + ? Parser::ConstantEvaluationContext::InFunctionSignature + : Parser::ConstantEvaluationContext::Default); } else { EnterExpressionEvaluationContext Unevaluated(Actions, Sema::ConstantEvaluated); Index: lib/Parse/ParseExpr.cpp =================================================================== --- lib/Parse/ParseExpr.cpp +++ lib/Parse/ParseExpr.cpp @@ -192,15 +192,29 @@ return ParseRHSOfBinaryExpression(R, prec::Assignment); } - -ExprResult Parser::ParseConstantExpression(TypeCastState isTypeCast) { +ExprResult Parser::ParseConstantExpression( + TypeCastState isTypeCast, + ConstantEvaluationContext ConstantEvalContext) { + + const Sema::ExpressionEvaluationContext EvalCtx = + [ConstantEvalContext] { + switch (ConstantEvalContext) { + case ConstantEvaluationContext::Default: + return Sema::ConstantEvaluated; + case ConstantEvaluationContext::InFunctionSignature: + return Sema::ConstantEvaluatedInFunctionSignature; + case ConstantEvaluationContext::InTemplateArgument: + return Sema::ConstantEvaluatedInTemplateArgument; + } + llvm_unreachable("all cases should be handled above"); + }(); + // C++03 [basic.def.odr]p2: // An expression is potentially evaluated unless it appears where an // integral constant expression is required (see 5.19) [...]. // C++98 and C++11 have no such rule, but this is only a defect in C++98. - EnterExpressionEvaluationContext ConstantEvaluated(Actions, - Sema::ConstantEvaluated); - + EnterExpressionEvaluationContext ConstantEvaluated( + Actions, EvalCtx); ExprResult LHS(ParseCastExpression(false, false, isTypeCast)); ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::Conditional)); return Actions.ActOnConstantExpression(Res); Index: lib/Parse/ParseTemplate.cpp =================================================================== --- lib/Parse/ParseTemplate.cpp +++ lib/Parse/ParseTemplate.cpp @@ -701,8 +701,8 @@ // end of the template-parameter-list rather than a greater-than // operator. GreaterThanIsOperatorScope G(GreaterThanIsOperator, false); - EnterExpressionEvaluationContext ConstantEvaluated(Actions, - Sema::ConstantEvaluated); + EnterExpressionEvaluationContext ConstantEvaluated( + Actions, Sema::ConstantEvaluatedInTemplateArgument); DefaultArg = Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()); if (DefaultArg.isInvalid()) @@ -1221,7 +1221,8 @@ // Parse a non-type template argument. SourceLocation Loc = Tok.getLocation(); - ExprResult ExprArg = ParseConstantExpression(MaybeTypeCast); + ExprResult ExprArg = ParseConstantExpression( + MaybeTypeCast, ConstantEvaluationContext::InTemplateArgument); if (ExprArg.isInvalid() || !ExprArg.get()) return ParsedTemplateArgument(); Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -13112,19 +13112,30 @@ unsigned NumTypos = Rec.NumTypos; if (!Rec.Lambdas.empty()) { - if (Rec.isUnevaluated() || Rec.Context == ConstantEvaluated) { - unsigned D; + if (Rec.isUnevaluated() || Rec.isConstantEvaluated()) { + unsigned D = 0; if (Rec.isUnevaluated()) { // C++11 [expr.prim.lambda]p2: // A lambda-expression shall not appear in an unevaluated operand // (Clause 5). D = diag::err_lambda_unevaluated_operand; - } else { - // C++1y [expr.const]p2: - // A conditional-expression e is a core constant expression unless the - // evaluation of e, following the rules of the abstract machine, would - // evaluate [...] a lambda-expression. - D = diag::err_lambda_in_constant_expression; + } else { + // C++1z allows lambda expressions in constant expressions that are not + // within template arguments or function signatures. + if (getLangOpts().CPlusPlus1z) { + if (Rec.Context == Sema::ConstantEvaluatedInFunctionSignature) + D = diag::err_lambda_in_function_signature; + else if (Rec.Context == Sema::ConstantEvaluatedInTemplateArgument) + D = diag::err_lambda_in_template_argument; + } else { + // C++14 [expr.const]p2: + // A conditional-expression e is a core constant expression unless + // the + // evaluation of e, following the rules of the abstract machine, + // would + // evaluate [...] a lambda-expression. + D = diag::err_lambda_in_constant_expression; + } } // C++1z allows lambda expressions as core constant expressions. @@ -13133,9 +13144,10 @@ // are part of function-signatures. Be mindful that P0315 (Lambdas in // unevaluated contexts) might lift some of these restrictions in a // future version. - if (Rec.Context != ConstantEvaluated || !getLangOpts().CPlusPlus1z) + if (D) { for (const auto *L : Rec.Lambdas) Diag(L->getLocStart(), D); + } } else { // Mark the capture expressions odr-used. This was deferred // during lambda expression creation. @@ -13150,7 +13162,7 @@ // temporaries that we may have created as part of the evaluation of // the expression in that context: they aren't relevant because they // will never be constructed. - if (Rec.isUnevaluated() || Rec.Context == ConstantEvaluated) { + if (Rec.isUnevaluated() || Rec.isConstantEvaluated()) { ExprCleanupObjects.erase(ExprCleanupObjects.begin() + Rec.NumCleanupObjects, ExprCleanupObjects.end()); Cleanup = Rec.ParentCleanup; @@ -13200,6 +13212,8 @@ case Sema::UnevaluatedList: case Sema::ConstantEvaluated: + case Sema::ConstantEvaluatedInTemplateArgument: + case Sema::ConstantEvaluatedInFunctionSignature: case Sema::PotentiallyEvaluated: // Expressions in this context could be evaluated. return true; @@ -13229,6 +13243,8 @@ return false; case Sema::ConstantEvaluated: + case Sema::ConstantEvaluatedInTemplateArgument: + case Sema::ConstantEvaluatedInFunctionSignature: case Sema::PotentiallyEvaluated: return true; @@ -14519,6 +14535,9 @@ break; case ConstantEvaluated: + case ConstantEvaluatedInTemplateArgument: + case ConstantEvaluatedInFunctionSignature: + // Relevant diagnostics should be produced by constant evaluation. break; Index: lib/Sema/SemaExprMember.cpp =================================================================== --- lib/Sema/SemaExprMember.cpp +++ lib/Sema/SemaExprMember.cpp @@ -145,6 +145,8 @@ case Sema::DiscardedStatement: case Sema::ConstantEvaluated: + case Sema::ConstantEvaluatedInFunctionSignature: + case Sema::ConstantEvaluatedInTemplateArgument: case Sema::PotentiallyEvaluated: case Sema::PotentiallyEvaluatedIfUsed: break; Index: lib/Sema/SemaLambda.cpp =================================================================== --- lib/Sema/SemaLambda.cpp +++ lib/Sema/SemaLambda.cpp @@ -1591,11 +1591,12 @@ // evaluation of e, following the rules of the abstract machine, would // evaluate [...] a lambda-expression. // - // This is technically incorrect, there are some constant evaluated contexts - // where this should be allowed. We should probably fix this when DR1607 is - // ratified, it lays out the exact set of conditions where we shouldn't - // allow a lambda-expression. + // In C++1z, lambda-expressions are allowed in certain contexts - which + // is handled when the expression evaluation context is popped off. case ConstantEvaluated: + case ConstantEvaluatedInFunctionSignature: + case ConstantEvaluatedInTemplateArgument: + // We don't actually diagnose this case immediately, because we // could be within a context where we might find out later that // the expression is potentially evaluated (e.g., for typeid). Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -3556,8 +3556,8 @@ for (unsigned i = 0, e = Param->getDepth(); i != e; ++i) TemplateArgLists.addOuterTemplateArguments(None); - EnterExpressionEvaluationContext ConstantEvaluated(SemaRef, - Sema::ConstantEvaluated); + EnterExpressionEvaluationContext ConstantEvaluated( + SemaRef, Sema::ConstantEvaluatedInTemplateArgument); return SemaRef.SubstExpr(Param->getDefaultArgument(), TemplateArgLists); } @@ -5237,8 +5237,8 @@ // The initialization of the parameter from the argument is // a constant-evaluated context. - EnterExpressionEvaluationContext ConstantEvaluated(*this, - Sema::ConstantEvaluated); + EnterExpressionEvaluationContext ConstantEvaluated( + *this, Sema::ConstantEvaluatedInTemplateArgument); if (getLangOpts().CPlusPlus1z) { // C++1z [temp.arg.nontype]p1: Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2236,8 +2236,8 @@ Param->setInvalidDecl(); if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) { - EnterExpressionEvaluationContext ConstantEvaluated(SemaRef, - Sema::ConstantEvaluated); + EnterExpressionEvaluationContext ConstantEvaluated( + SemaRef, Sema::ConstantEvaluatedInTemplateArgument); ExprResult Value = SemaRef.SubstExpr(D->getDefaultArgument(), TemplateArgs); if (!Value.isInvalid()) Param->setDefaultArgument(Value.get()); Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -3787,7 +3787,8 @@ case TemplateArgument::Expression: { // Template argument expressions are constant expressions. EnterExpressionEvaluationContext Unevaluated( - getSema(), Uneval ? Sema::Unevaluated : Sema::ConstantEvaluated); + getSema(), + Uneval ? Sema::Unevaluated : Sema::ConstantEvaluatedInTemplateArgument); Expr *InputExpr = Input.getSourceExpression(); if (!InputExpr) InputExpr = Input.getArgument().getAsExpr(); Index: test/SemaCXX/cxx1z-constexpr-lambdas.cpp =================================================================== --- test/SemaCXX/cxx1z-constexpr-lambdas.cpp +++ test/SemaCXX/cxx1z-constexpr-lambdas.cpp @@ -157,6 +157,56 @@ } // end ns1_simple_lambda +namespace test_forbidden_lambda_expressions { + +template struct X { }; //expected-error{{lambda expression may not appear}} +X<[]{return 10; }()> x; //expected-error{{lambda expression may not appear}} +void f(int arr[([] { return 5; }())]); //expected-error{{lambda expression may not appear}} +// FIXME: Should this be ok? +auto L = [](int arr[([] { return 5; }())]) { }; // OK???? + +// These should be allowed: +struct A { + int : ([] { return 5; }()); +}; + +int arr[([] { return 5; }())]; +enum { E = [] { return 5; }() }; +static_assert([]{return 5; }() == 5); +int *ip = new int[([] { return 5; })()]; + +int test_case(int x) { + switch(x) { + case [] { return 5; }(): //OK + break; + case [] (auto a) { return a; }(6): //expected-note{{previous}} + break; + case 6: //expected-error{{duplicate}} + break; + } + return x; +} +namespace ns2 { +int (*f)(int a[([] { return 5; }())]); +int fun(int a[([] { return 5; }())]); //expected-error{{lambda expression}} +int fun2(void (*)(int a[([] { return 5; }())])); //expected-error{{lambda expression}} +int (*fun2p)(void f(int a[([] { return 5; }())])); + +int (*g(int))[([] { return 5; })()]; //expected-error{{lambda expression}} +int *(fun3)(int a[([] { return 5; }())]); //expected-error{{lambda expression}} + + +int *(&fun3r)(int a[([] { return 5; }())]) = ([] () -> auto& { int *fv(int *); return fv; })(); + +int (*g2(int))[([] { return 5; })()]; //expected-error{{lambda expression}} + +int (*(*g3)(int))[([] { return 5; })()]; +template struct X { }; +X x; //expected-error{{lambda expression}} +} //end ns2 + +} // end ns forbidden_lambda_expressions + namespace ns1_unimplemented { namespace ns1_captures { constexpr auto f(int i) {