[Cxx1z-constexpr-lambda-P0170R1] Support parsing of constexpr specifier (and its inference) on lambda expressions
Support the constexpr specifier on lambda expressions - and support its inference from the lambda call operator's body. i.e. auto L = [] () constexpr { return 5; }; static_assert(L() == 5); // OK auto Implicit = [] (auto a) { return a; }; static_assert(Implicit(5) == 5); We do not support evaluation of lambda's within constant expressions just yet. Implementation Strategy: - teach ParseLambdaExpressionAfterIntroducer to expect a constexpr specifier and mark the invented function call operator's declarator's decl-specifier with it; Have it emit fixits for multiple decl-specifiers (mutable or constexpr) in this location. - for cases where constexpr is not explicitly specified, have buildLambdaExpr check whether the invented function call operator satisfies the requirements of a constexpr function, by calling CheckConstexprFunctionDecl/Body. Much obliged to Richard Smith for his patience and his care, in ensuring the code is clang-worthy. llvm-svn: 264513
This commit is contained in:
parent
e4dbeb40c6
commit
a734ab9808
|
@ -153,6 +153,12 @@ def warn_integer_constant_overflow : Warning<
|
|||
"overflow in expression; result is %0 with type %1">,
|
||||
InGroup<DiagGroup<"integer-overflow">>;
|
||||
|
||||
// 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<
|
||||
|
|
|
@ -765,11 +765,20 @@ def warn_cxx98_compat_lambda : Warning<
|
|||
InGroup<CXX98Compat>, DefaultIgnore;
|
||||
def err_lambda_missing_parens : Error<
|
||||
"lambda requires '()' before %select{'mutable'|return type|"
|
||||
"attribute specifier}0">;
|
||||
"attribute specifier|'constexpr'}0">;
|
||||
def err_lambda_decl_specifier_repeated : Error<
|
||||
"%select{'mutable'|'constexpr'}0 cannot appear multiple times in a lambda declarator">;
|
||||
// C++1z lambda expressions
|
||||
def err_expected_star_this_capture : Error<
|
||||
"expected 'this' following '*' in lambda capture list">;
|
||||
|
||||
// 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<CXXPre1zCompat>, DefaultIgnore;
|
||||
def ext_constexpr_on_lambda_cxx1z : ExtWarn<
|
||||
"'constexpr' on lambda expressions is a C++1z extension">, InGroup<CXX1z>;
|
||||
|
||||
// Availability attribute
|
||||
def err_expected_version : Error<
|
||||
"expected a version of the form 'major[.minor[.subminor]]'">;
|
||||
|
|
|
@ -5013,7 +5013,8 @@ public:
|
|||
SourceRange IntroducerRange,
|
||||
TypeSourceInfo *MethodType,
|
||||
SourceLocation EndLoc,
|
||||
ArrayRef<ParmVarDecl *> Params);
|
||||
ArrayRef<ParmVarDecl *> Params,
|
||||
bool IsConstexprSpecified);
|
||||
|
||||
/// \brief Endow the lambda scope info with the relevant properties.
|
||||
void buildLambdaScope(sema::LambdaScopeInfo *LSI,
|
||||
|
|
|
@ -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,7 +2046,22 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
|
|||
// If this is a local variable, dig out its value.
|
||||
if (Frame) {
|
||||
Result = Frame->getTemporary(VD);
|
||||
assert(Result && "missing value for local variable");
|
||||
if (!Result) {
|
||||
// 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.
|
||||
assert(isLambdaCallOperator(Frame->Callee) &&
|
||||
(VD->getDeclContext() != Frame->Callee || VD->isInitCapture()) &&
|
||||
"missing value for local variable");
|
||||
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;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1050,6 +1050,58 @@ bool Parser::TryParseLambdaIntroducer(LambdaIntroducer &Intro) {
|
|||
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_decl_specifier_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_decl_specifier_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(
|
||||
|
@ -1108,10 +1160,13 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
|
|||
// 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;
|
||||
|
@ -1169,7 +1224,8 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
|
|||
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.
|
||||
|
@ -1179,6 +1235,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
|
|||
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");
|
||||
}
|
||||
|
||||
|
|
|
@ -355,7 +355,8 @@ CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class,
|
|||
SourceRange IntroducerRange,
|
||||
TypeSourceInfo *MethodTypeInfo,
|
||||
SourceLocation EndLoc,
|
||||
ArrayRef<ParmVarDecl *> Params) {
|
||||
ArrayRef<ParmVarDecl *> Params,
|
||||
const bool IsConstexprSpecified) {
|
||||
QualType MethodType = MethodTypeInfo->getType();
|
||||
TemplateParameterList *TemplateParams =
|
||||
getGenericLambdaTemplateParameterList(getCurLambda(), *this);
|
||||
|
@ -392,7 +393,7 @@ CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class,
|
|||
MethodType, MethodTypeInfo,
|
||||
SC_None,
|
||||
/*isInline=*/true,
|
||||
/*isConstExpr=*/false,
|
||||
IsConstexprSpecified,
|
||||
EndLoc);
|
||||
Method->setAccess(AS_public);
|
||||
|
||||
|
@ -879,8 +880,9 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
|
|||
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);
|
||||
|
||||
|
@ -1600,6 +1602,17 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
|
|||
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) {
|
||||
|
|
|
@ -10054,7 +10054,9 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
|
|||
CXXMethodDecl *NewCallOperator = getSema().startLambdaDefinition(
|
||||
Class, E->getIntroducerRange(), NewCallOpTSI,
|
||||
E->getCallOperator()->getLocEnd(),
|
||||
NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams());
|
||||
NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams(),
|
||||
E->getCallOperator()->isConstexpr());
|
||||
|
||||
LSI->CallOperator = NewCallOperator;
|
||||
|
||||
getDerived().transformAttrs(E->getCallOperator(), NewCallOperator);
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// 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 //expected-error{{cannot appear multiple times}}
|
||||
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 { };
|
||||
auto XL16 = [] () constexpr
|
||||
mutable
|
||||
constexpr //expected-error{{cannot appear multiple times}}
|
||||
mutable //expected-error{{cannot appear multiple times}}
|
||||
mutable //expected-error{{cannot appear multiple times}}
|
||||
constexpr //expected-error{{cannot appear multiple times}}
|
||||
constexpr //expected-error{{cannot appear multiple times}}
|
||||
{ };
|
||||
|
||||
#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
|
||||
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// 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 { asm("non-constexpr"); }; //expected-error{{not allowed in 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
|
||||
namespace ns2 {
|
||||
auto L = [](auto a) { return a; };
|
||||
static_assert(L(3) == 3);
|
||||
static_assert(L(3.14) == 3.14);
|
||||
}
|
||||
namespace ns3 {
|
||||
auto L = [](auto a) { asm("non-constexpr"); return a; }; //expected-note{{declared here}}
|
||||
constexpr int I = //expected-error{{must be initialized by a constant expression}}
|
||||
L(3); //expected-note{{non-constexpr function}}
|
||||
}
|
||||
|
||||
} // end ns test_constexpr_call
|
Loading…
Reference in New Issue