Index: include/clang/Basic/DiagnosticASTKinds.td =================================================================== --- include/clang/Basic/DiagnosticASTKinds.td +++ include/clang/Basic/DiagnosticASTKinds.td @@ -153,6 +153,12 @@ "overflow in expression; result is %0 with type %1">, InGroup>; +// This is a temporary diagnostic, and shall be removed once our +// implementation is complete, and like the preceding constexpr notes belongs +// in Sema. +def note_unimplemented_constexpr_lambda_feature_ast : Note< + "unimplemented constexpr lambda feature: %0 (coming soon!)">; + // inline asm related. let CategoryName = "Inline Assembly Issue" in { def err_asm_invalid_escape : Error< Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -765,8 +765,15 @@ InGroup, DefaultIgnore; def err_lambda_missing_parens : Error< "lambda requires '()' before %select{'mutable'|return type|" - "attribute specifier}0">; + "attribute specifier|'constexpr'}0">; +// C++1z constexpr lambda expressions +def warn_cxx14_compat_constexpr_on_lambda : Warning< + "constexpr on lambda expressions are incompatible with C++14">, + InGroup, DefaultIgnore; +def err_constexpr_on_lambda : Error< + "constexpr on lambda expressions is permitted only in C++1z">; + // Availability attribute def err_expected_version : Error< "expected a version of the form 'major[.minor[.subminor]]'">; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -4957,7 +4957,8 @@ SourceRange IntroducerRange, TypeSourceInfo *MethodType, SourceLocation EndLoc, - ArrayRef Params); + ArrayRef Params, + bool IsConstexprSpecified); /// \brief Endow the lambda scope info with the relevant properties. void buildLambdaScope(sema::LambdaScopeInfo *LSI, Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -36,6 +36,7 @@ #include "clang/AST/APValue.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" +#include "clang/AST/ASTLambda.h" #include "clang/AST/CharUnits.h" #include "clang/AST/Expr.h" #include "clang/AST/RecordLayout.h" @@ -2009,6 +2010,20 @@ // If this is a local variable, dig out its value. if (Frame) { Result = Frame->getTemporary(VD); + if (!Result && isLambdaCallOperator(Frame->Callee) && + VD->getDeclContext() != Frame->Callee) { + // Assume variables referenced within a lambda's call operator that were + // not declared within the call operator are captures and during checking + // of a potential constant expression, assume they are unknown constant + // expressions. + if (Info.checkingPotentialConstantExpression()) + return false; + // FIXME: implement capture evaluation during constant expr evaluation. + Info.Diag(E->getLocStart(), + diag::note_unimplemented_constexpr_lambda_feature_ast) + << "captures not currently allowed"; + return false; + } assert(Result && "missing value for local variable"); return true; } Index: lib/Parse/ParseExprCXX.cpp =================================================================== --- lib/Parse/ParseExprCXX.cpp +++ lib/Parse/ParseExprCXX.cpp @@ -1042,6 +1042,36 @@ return false; } +static inline void +TryConsumeMutableOrConstexprToken(Parser &P, SourceLocation &MutableLoc, + SourceLocation &ConstexprLoc, + SourceLocation &DeclEndLoc, DeclSpec &DS) { + if (P.TryConsumeToken(tok::kw_mutable, MutableLoc)) { + if (P.TryConsumeToken(tok::kw_constexpr, ConstexprLoc)) + DeclEndLoc = ConstexprLoc; + else + DeclEndLoc = MutableLoc; + } else if (P.TryConsumeToken(tok::kw_constexpr, ConstexprLoc)) { + if (P.TryConsumeToken(tok::kw_mutable, MutableLoc)) + DeclEndLoc = MutableLoc; + else + DeclEndLoc = ConstexprLoc; + } + + if (ConstexprLoc.isValid()) { + if (!P.getLangOpts().CPlusPlus1z) { + P.Diag(ConstexprLoc, diag::err_constexpr_on_lambda); + } else { + P.Diag(ConstexprLoc, diag::warn_cxx14_compat_constexpr_on_lambda); + const char *PrevSpec = nullptr; + unsigned DiagID = 0; + DS.SetConstexprSpec(ConstexprLoc, PrevSpec, DiagID); + assert(PrevSpec == nullptr && DiagID == 0 && + "Constexpr could not have been set previously!"); + } + } +} + /// ParseLambdaExpressionAfterIntroducer - Parse the rest of a lambda /// expression. ExprResult Parser::ParseLambdaExpressionAfterIntroducer( @@ -1100,10 +1130,12 @@ // compatible with MSVC. MaybeParseMicrosoftDeclSpecs(Attr, &DeclEndLoc); - // Parse 'mutable'[opt]. + // Parse mutable-opt and/or constexpr-opt, and update the DeclSpec with the + // constexpr status. SourceLocation MutableLoc; - if (TryConsumeToken(tok::kw_mutable, MutableLoc)) - DeclEndLoc = MutableLoc; + SourceLocation ConstexprLoc; + TryConsumeMutableOrConstexprToken(*this, MutableLoc, ConstexprLoc, + DeclEndLoc, DS); // Parse exception-specification[opt]. ExceptionSpecificationType ESpecType = EST_None; @@ -1161,7 +1193,8 @@ LParenLoc, FunLocalRangeEnd, D, TrailingReturnType), Attr, DeclEndLoc); - } else if (Tok.isOneOf(tok::kw_mutable, tok::arrow, tok::kw___attribute) || + } else if (Tok.isOneOf(tok::kw_mutable, tok::arrow, tok::kw___attribute, + tok::kw_constexpr) || (Tok.is(tok::l_square) && NextToken().is(tok::l_square))) { // It's common to forget that one needs '()' before 'mutable', an attribute // specifier, or the result type. Deal with this. @@ -1171,6 +1204,7 @@ case tok::arrow: TokKind = 1; break; case tok::kw___attribute: case tok::l_square: TokKind = 2; break; + case tok::kw_constexpr: TokKind = 3; break; default: llvm_unreachable("Unknown token kind"); } Index: lib/Sema/SemaLambda.cpp =================================================================== --- lib/Sema/SemaLambda.cpp +++ lib/Sema/SemaLambda.cpp @@ -354,7 +354,8 @@ SourceRange IntroducerRange, TypeSourceInfo *MethodTypeInfo, SourceLocation EndLoc, - ArrayRef Params) { + ArrayRef Params, + const bool IsConstexprSpecified) { QualType MethodType = MethodTypeInfo->getType(); TemplateParameterList *TemplateParams = getGenericLambdaTemplateParameterList(getCurLambda(), *this); @@ -391,7 +392,7 @@ MethodType, MethodTypeInfo, SC_None, /*isInline=*/true, - /*isConstExpr=*/false, + IsConstexprSpecified, EndLoc); Method->setAccess(AS_public); @@ -881,8 +882,9 @@ CXXRecordDecl *Class = createLambdaClosureType(Intro.Range, MethodTyInfo, KnownDependent, Intro.Default); - CXXMethodDecl *Method = startLambdaDefinition(Class, Intro.Range, - MethodTyInfo, EndLoc, Params); + CXXMethodDecl *Method = + startLambdaDefinition(Class, Intro.Range, MethodTyInfo, EndLoc, Params, + ParamInfo.getDeclSpec().isConstexprSpecified()); if (ExplicitParams) CheckCXXDefaultArguments(Method); @@ -1588,6 +1590,17 @@ CaptureInits, ArrayIndexVars, ArrayIndexStarts, EndLoc, ContainsUnexpandedParameterPack); + // If the lambda expression's call operator is not explicitly marked constexpr + // and we are not in a dependent context, analyze the call operator to infer + // its constexpr-ness, supressing diagnostics while doing so. + if (getLangOpts().CPlusPlus1z && !CallOperator->isInvalidDecl() && + !CallOperator->isConstexpr() && + !Class->getDeclContext()->isDependentContext()) { + TentativeAnalysisScope DiagnosticScopeGuard(*this); + CallOperator->setConstexpr( + CheckConstexprFunctionDecl(CallOperator) && + CheckConstexprFunctionBody(CallOperator, CallOperator->getBody())); + } if (!CurContext->isDependentContext()) { switch (ExprEvalContexts.back().Context) { Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -9678,7 +9678,9 @@ CXXMethodDecl *NewCallOperator = getSema().startLambdaDefinition( Class, E->getIntroducerRange(), NewCallOpTSI, E->getCallOperator()->getLocEnd(), - NewCallOpTSI->getTypeLoc().castAs().getParams()); + NewCallOpTSI->getTypeLoc().castAs().getParams(), + E->getCallOperator()->isConstexpr()); + LSI->CallOperator = NewCallOperator; getDerived().transformAttrs(E->getCallOperator(), NewCallOperator); Index: test/Parser/cxx1z-constexpr-lambdas.cpp =================================================================== --- test/Parser/cxx1z-constexpr-lambdas.cpp +++ test/Parser/cxx1z-constexpr-lambdas.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -std=c++1z %s -verify -DCPP1Z +// RUN: %clang_cc1 -std=c++14 %s -verify -DCPP14 + +auto L0 = [] constexpr { }; //expected-error{{requires '()'}} expected-error{{expected body}} +#ifdef CPP1Z + +auto L = []() mutable constexpr { }; +auto L2 = []() constexpr { }; +auto L3 = []() mutable { }; +auto L4 = []() constexpr mutable { }; + + +#elif defined(CPP14) +auto L = [] () constexpr { }; //expected-error{{only in C++1z}} + +#endif + + Index: test/SemaCXX/cxx1z-constexpr-lambdas.cpp =================================================================== --- test/SemaCXX/cxx1z-constexpr-lambdas.cpp +++ test/SemaCXX/cxx1z-constexpr-lambdas.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks %s +// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -fdelayed-template-parsing %s -DDELAYED_TEMPLATE_PARSING +// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -fms-extensions %s -DMS_EXTENSIONS +// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -fdelayed-template-parsing -fms-extensions %s -DMS_EXTENSIONS -DDELAYED_TEMPLATE_PARSING + +namespace test_constexpr_checking { + +namespace ns1 { + struct NonLit { ~NonLit(); }; //expected-note{{not literal}} + auto L = [](NonLit NL) constexpr { }; //expected-error{{not a literal type}} +} // end ns1 + +namespace ns2 { + auto L = [](int I) constexpr { static int J; }; //expected-error{{not permitted in a constexpr function}} +} // end ns1 + +} // end ns test_constexpr_checking + +namespace test_constexpr_call { + +namespace ns1 { + auto L = [](int I) { return I; }; + static_assert(L(3) == 3); +} // end ns1 + +} // end ns test_constexpr_call \ No newline at end of file