Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6278,6 +6278,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/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 @@ -6247,7 +6247,11 @@ // Parse the constant-expression or assignment-expression now (depending // on dialect). if (getLangOpts().CPlusPlus) { - NumElements = ParseConstantExpression(); + NumElements = ParseConstantExpression( + NotTypeCast, + D.getContext() == D.PrototypeContext + ? 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()) @@ -1220,7 +1220,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 @@ -13104,19 +13104,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. @@ -13125,9 +13136,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. @@ -13142,7 +13154,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; @@ -13192,6 +13204,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; @@ -13221,6 +13235,8 @@ return false; case Sema::ConstantEvaluated: + case Sema::ConstantEvaluatedInTemplateArgument: + case Sema::ConstantEvaluatedInFunctionSignature: case Sema::PotentiallyEvaluated: return true; @@ -14511,6 +14527,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 @@ -3520,8 +3520,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); } @@ -5152,8 +5152,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 @@ -3781,7 +3781,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,38 @@ } // 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; +} + +} // end ns forbidden_lambda_expressions + namespace ns1_unimplemented { namespace ns1_captures { constexpr auto f(int i) {