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,7 +765,16 @@ InGroup, DefaultIgnore; def err_lambda_missing_parens : Error< "lambda requires '()' before %select{'mutable'|return type|" - "attribute specifier}0">; + "attribute specifier|'constexpr'}0">; +def err_lambda_declspecifier_repeated : Error< + "%select{'mutable'|'constexpr'}0 cannot appear multiple times in a lambda declarator">; + +// C++1z constexpr lambda expressions +def warn_cxx14_compat_constexpr_on_lambda : Warning< + "constexpr on lambda expressions is incompatible with C++ standards before C++1z">, + InGroup, DefaultIgnore; +def ext_constexpr_on_lambda_cxx1z : ExtWarn< + "'constexpr' on lambda expressions is a C++1z extension">, InGroup; // Availability attribute def err_expected_version : Error< Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -5006,7 +5006,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" @@ -2045,6 +2046,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 || VD->isInitCapture())) { + // 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,58 @@ return false; } +static void +tryConsumeMutableOrConstexprToken(Parser &P, SourceLocation &MutableLoc, + SourceLocation &ConstexprLoc, + SourceLocation &DeclEndLoc) { + assert(MutableLoc.isInvalid()); + assert(ConstexprLoc.isInvalid()); + // Consume constexpr-opt mutable-opt in any sequence, and set the DeclEndLoc + // to the final of those locations. Emit an error if we have multiple + // copies of those keywords and recover. + + while (true) { + switch (P.getCurToken().getKind()) { + case tok::kw_mutable: { + if (MutableLoc.isValid()) { + P.Diag(P.getCurToken().getLocation(), + diag::err_lambda_declspecifier_repeated) + << 0 << FixItHint::CreateRemoval(P.getCurToken().getLocation()); + } + MutableLoc = P.ConsumeToken(); + DeclEndLoc = MutableLoc; + break /*switch*/; + } + case tok::kw_constexpr: + if (ConstexprLoc.isValid()) { + P.Diag(P.getCurToken().getLocation(), + diag::err_lambda_declspecifier_repeated) + << 1 << FixItHint::CreateRemoval(P.getCurToken().getLocation()); + } + ConstexprLoc = P.ConsumeToken(); + DeclEndLoc = ConstexprLoc; + break /*switch*/; + default: + return ; + } + } +} + +static void +addConstexprToLambdaDeclSpecifier(Parser &P, SourceLocation ConstexprLoc, + DeclSpec &DS) { + if (ConstexprLoc.isValid()) { + P.Diag(ConstexprLoc, !P.getLangOpts().CPlusPlus1z + ? diag::ext_constexpr_on_lambda_cxx1z + : 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 cannot have been set previously!"); + } +} + /// ParseLambdaExpressionAfterIntroducer - Parse the rest of a lambda /// expression. ExprResult Parser::ParseLambdaExpressionAfterIntroducer( @@ -1100,10 +1152,13 @@ // compatible with MSVC. MaybeParseMicrosoftDeclSpecs(Attr, &DeclEndLoc); - // Parse 'mutable'[opt]. + // Parse mutable-opt and/or constexpr-opt, and update the DeclEndLoc. SourceLocation MutableLoc; - if (TryConsumeToken(tok::kw_mutable, MutableLoc)) - DeclEndLoc = MutableLoc; + SourceLocation ConstexprLoc; + tryConsumeMutableOrConstexprToken(*this, MutableLoc, ConstexprLoc, + DeclEndLoc); + + addConstexprToLambdaDeclSpecifier(*this, ConstexprLoc, DS); // Parse exception-specification[opt]. ExceptionSpecificationType ESpecType = EST_None; @@ -1161,7 +1216,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 +1227,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 @@ -355,7 +355,8 @@ SourceRange IntroducerRange, TypeSourceInfo *MethodTypeInfo, SourceLocation EndLoc, - ArrayRef Params) { + ArrayRef Params, + const bool IsConstexprSpecified) { QualType MethodType = MethodTypeInfo->getType(); TemplateParameterList *TemplateParams = getGenericLambdaTemplateParameterList(getCurLambda(), *this); @@ -392,7 +393,7 @@ MethodType, MethodTypeInfo, SC_None, /*isInline=*/true, - /*isConstExpr=*/false, + IsConstexprSpecified, EndLoc); Method->setAccess(AS_public); @@ -884,8 +885,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); @@ -1597,6 +1599,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 @@ -10054,7 +10054,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,20 @@ +// RUN: %clang_cc1 -std=c++1z %s -verify +// RUN: %clang_cc1 -std=c++14 %s -verify +// RUN: %clang_cc1 -std=c++11 %s -verify + + +auto XL0 = [] constexpr { }; //expected-error{{requires '()'}} expected-error{{expected body}} +auto XL1 = [] () mutable mutable mutable { }; //expected-error{{cannot appear multiple times}} + +#if __cplusplus > 201402L +auto XL2 = [] () constexpr mutable constexpr { }; //expected-error{{cannot appear multiple times}} +auto L = []() mutable constexpr { }; +auto L2 = []() constexpr { }; +auto L4 = []() constexpr mutable { }; +#else +auto L = []() mutable constexpr {return 0; }; //expected-warning{{is a C++1z extension}} +auto L2 = []() constexpr { return 0;};//expected-warning{{is a C++1z extension}} +auto L4 = []() constexpr mutable { return 0; }; //expected-warning{{is a C++1z extension}} +#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 +// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -fms-extensions %s +// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -fdelayed-template-parsing -fms-extensions %s + +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