Skip to content

Commit

Permalink
[C++17] Disallow lambdas in template parameters (PR33696).
Browse files Browse the repository at this point in the history
Summary: This revision disallows lambdas in template parameters, as reported in PR33696.

Reviewers: rsmith

Reviewed By: rsmith

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D37442

llvm-svn: 336930
  • Loading branch information
Rakete1111 committed Jul 12, 2018
1 parent 9436570 commit b6d5c58
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 60 deletions.
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -6493,6 +6493,8 @@ let CategoryName = "Lambda Issue" in {
"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_invalid_context : Error<
"a lambda expression cannot appear in this context">;
def err_lambda_return_init_list : Error<
"cannot deduce lambda return type from initializer list">;
def err_lambda_capture_default_arg : Error<
Expand Down
62 changes: 35 additions & 27 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -979,15 +979,21 @@ class Sema {
/// expressions for which we have deferred checking the destructor.
SmallVector<CXXBindTemporaryExpr *, 8> DelayedDecltypeBinds;

/// \brief Describes whether we are in an expression constext which we have
/// to handle differently.
enum ExpressionKind {
EK_Decltype, EK_TemplateArgument, EK_Other
} ExprContext;

ExpressionEvaluationContextRecord(ExpressionEvaluationContext Context,
unsigned NumCleanupObjects,
CleanupInfo ParentCleanup,
Decl *ManglingContextDecl,
bool IsDecltype)
: Context(Context), ParentCleanup(ParentCleanup),
IsDecltype(IsDecltype), NumCleanupObjects(NumCleanupObjects),
NumTypos(0),
ManglingContextDecl(ManglingContextDecl), MangleNumbering() { }
ExpressionKind ExprContext)
: Context(Context), ParentCleanup(ParentCleanup),
NumCleanupObjects(NumCleanupObjects), NumTypos(0),
ManglingContextDecl(ManglingContextDecl), MangleNumbering(),
ExprContext(ExprContext) {}

/// Retrieve the mangling numbering context, used to consistently
/// number constructs like lambdas for mangling.
Expand Down Expand Up @@ -3989,13 +3995,15 @@ class Sema {
void DiagnoseSentinelCalls(NamedDecl *D, SourceLocation Loc,
ArrayRef<Expr *> Args);

void PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
Decl *LambdaContextDecl = nullptr,
bool IsDecltype = false);
void PushExpressionEvaluationContext(
ExpressionEvaluationContext NewContext, Decl *LambdaContextDecl = nullptr,
ExpressionEvaluationContextRecord::ExpressionKind Type =
ExpressionEvaluationContextRecord::EK_Other);
enum ReuseLambdaContextDecl_t { ReuseLambdaContextDecl };
void PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
ReuseLambdaContextDecl_t,
bool IsDecltype = false);
void PushExpressionEvaluationContext(
ExpressionEvaluationContext NewContext, ReuseLambdaContextDecl_t,
ExpressionEvaluationContextRecord::ExpressionKind Type =
ExpressionEvaluationContextRecord::EK_Other);
void PopExpressionEvaluationContext();

void DiscardCleanupsInEvaluationContext();
Expand Down Expand Up @@ -10748,25 +10756,25 @@ class EnterExpressionEvaluationContext {
bool Entered = true;

public:

EnterExpressionEvaluationContext(Sema &Actions,
Sema::ExpressionEvaluationContext NewContext,
Decl *LambdaContextDecl = nullptr,
bool IsDecltype = false,
bool ShouldEnter = true)
EnterExpressionEvaluationContext(
Sema &Actions, Sema::ExpressionEvaluationContext NewContext,
Decl *LambdaContextDecl = nullptr,
Sema::ExpressionEvaluationContextRecord::ExpressionKind ExprContext =
Sema::ExpressionEvaluationContextRecord::EK_Other,
bool ShouldEnter = true)
: Actions(Actions), Entered(ShouldEnter) {
if (Entered)
Actions.PushExpressionEvaluationContext(NewContext, LambdaContextDecl,
IsDecltype);
ExprContext);
}
EnterExpressionEvaluationContext(Sema &Actions,
Sema::ExpressionEvaluationContext NewContext,
Sema::ReuseLambdaContextDecl_t,
bool IsDecltype = false)
: Actions(Actions) {
Actions.PushExpressionEvaluationContext(NewContext,
Sema::ReuseLambdaContextDecl,
IsDecltype);
EnterExpressionEvaluationContext(
Sema &Actions, Sema::ExpressionEvaluationContext NewContext,
Sema::ReuseLambdaContextDecl_t,
Sema::ExpressionEvaluationContextRecord::ExpressionKind ExprContext =
Sema::ExpressionEvaluationContextRecord::EK_Other)
: Actions(Actions) {
Actions.PushExpressionEvaluationContext(
NewContext, Sema::ReuseLambdaContextDecl, ExprContext);
}

enum InitListTag { InitList };
Expand All @@ -10780,7 +10788,7 @@ class EnterExpressionEvaluationContext {
if (ShouldEnter && Actions.isUnevaluatedContext() &&
Actions.getLangOpts().CPlusPlus11) {
Actions.PushExpressionEvaluationContext(
Sema::ExpressionEvaluationContext::UnevaluatedList, nullptr, false);
Sema::ExpressionEvaluationContext::UnevaluatedList);
Entered = true;
}
}
Expand Down
4 changes: 1 addition & 3 deletions clang/lib/Parse/ParseDecl.cpp
Expand Up @@ -309,9 +309,7 @@ unsigned Parser::ParseAttributeArgsCommon(
EnterExpressionEvaluationContext Unevaluated(
Actions,
Uneval ? Sema::ExpressionEvaluationContext::Unevaluated
: Sema::ExpressionEvaluationContext::ConstantEvaluated,
/*LambdaContextDecl=*/nullptr,
/*IsDecltype=*/false);
: Sema::ExpressionEvaluationContext::ConstantEvaluated);

ExprResult ArgExpr(
Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Parse/ParseDeclCXX.cpp
Expand Up @@ -942,7 +942,7 @@ SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) {
// The operand of the decltype specifier is an unevaluated operand.
EnterExpressionEvaluationContext Unevaluated(
Actions, Sema::ExpressionEvaluationContext::Unevaluated, nullptr,
/*IsDecltype=*/true);
Sema::ExpressionEvaluationContextRecord::EK_Decltype);
Result =
Actions.CorrectDelayedTyposInExpr(ParseExpression(), [](Expr *E) {
return E->hasPlaceholderType() ? ExprError() : E;
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Parse/ParseStmt.cpp
Expand Up @@ -1196,7 +1196,7 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
{
EnterExpressionEvaluationContext PotentiallyDiscarded(
Actions, Sema::ExpressionEvaluationContext::DiscardedStatement, nullptr,
false,
Sema::ExpressionEvaluationContextRecord::EK_Other,
/*ShouldEnter=*/ConstexprCondition && !*ConstexprCondition);
ThenStmt = ParseStatement(&InnerStatementTrailingElseLoc);
}
Expand Down Expand Up @@ -1230,7 +1230,7 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {

EnterExpressionEvaluationContext PotentiallyDiscarded(
Actions, Sema::ExpressionEvaluationContext::DiscardedStatement, nullptr,
false,
Sema::ExpressionEvaluationContextRecord::EK_Other,
/*ShouldEnter=*/ConstexprCondition && *ConstexprCondition);
ElseStmt = ParseStatement();

Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Parse/ParseTemplate.cpp
Expand Up @@ -1235,7 +1235,9 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() {
// argument before trying to disambiguate.

EnterExpressionEvaluationContext EnterConstantEvaluated(
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated,
/*LambdaContextDecl=*/nullptr,
/*ExprContext=*/Sema::ExpressionEvaluationContextRecord::EK_TemplateArgument);
if (isCXXTypeId(TypeIdAsTemplateArgument)) {
TypeResult TypeArg = ParseTypeName(
/*Range=*/nullptr, DeclaratorContext::TemplateArgContext);
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/Sema.cpp
Expand Up @@ -163,7 +163,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,

ExprEvalContexts.emplace_back(
ExpressionEvaluationContext::PotentiallyEvaluated, 0, CleanupInfo{},
nullptr, false);
nullptr, ExpressionEvaluationContextRecord::EK_Other);

PreallocatedFunctionScope.reset(new FunctionScopeInfo(Diags));

Expand Down
43 changes: 22 additions & 21 deletions clang/lib/Sema/SemaExpr.cpp
Expand Up @@ -14132,53 +14132,53 @@ ExprResult Sema::TransformToPotentiallyEvaluated(Expr *E) {
}

void
Sema::PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
Decl *LambdaContextDecl,
bool IsDecltype) {
Sema::PushExpressionEvaluationContext(
ExpressionEvaluationContext NewContext, Decl *LambdaContextDecl,
ExpressionEvaluationContextRecord::ExpressionKind ExprContext) {
ExprEvalContexts.emplace_back(NewContext, ExprCleanupObjects.size(), Cleanup,
LambdaContextDecl, IsDecltype);
LambdaContextDecl, ExprContext);
Cleanup.reset();
if (!MaybeODRUseExprs.empty())
std::swap(MaybeODRUseExprs, ExprEvalContexts.back().SavedMaybeODRUseExprs);
}

void
Sema::PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
ReuseLambdaContextDecl_t,
bool IsDecltype) {
Sema::PushExpressionEvaluationContext(
ExpressionEvaluationContext NewContext, ReuseLambdaContextDecl_t,
ExpressionEvaluationContextRecord::ExpressionKind ExprContext) {
Decl *ClosureContextDecl = ExprEvalContexts.back().ManglingContextDecl;
PushExpressionEvaluationContext(NewContext, ClosureContextDecl, IsDecltype);
PushExpressionEvaluationContext(NewContext, ClosureContextDecl, ExprContext);
}

void Sema::PopExpressionEvaluationContext() {
ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back();
unsigned NumTypos = Rec.NumTypos;

if (!Rec.Lambdas.empty()) {
if (Rec.isUnevaluated() || Rec.isConstantEvaluated()) {
using ExpressionKind = ExpressionEvaluationContextRecord::ExpressionKind;
if (Rec.ExprContext == ExpressionKind::EK_TemplateArgument || Rec.isUnevaluated() ||
(Rec.isConstantEvaluated() && !getLangOpts().CPlusPlus17)) {
unsigned D;
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 {
} else if (Rec.isConstantEvaluated() && !getLangOpts().CPlusPlus17) {
// 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 if (Rec.ExprContext == ExpressionKind::EK_TemplateArgument) {
// C++17 [expr.prim.lamda]p2:
// A lambda-expression shall not appear [...] in a template-argument.
D = diag::err_lambda_in_invalid_context;
} else
llvm_unreachable("Couldn't infer lambda error message.");

// C++1z allows lambda expressions as core constant expressions.
// FIXME: In C++1z, reinstate the restrictions on lambda expressions (CWG
// 1607) from appearing within template-arguments and array-bounds that
// 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.isConstantEvaluated() || !getLangOpts().CPlusPlus17)
for (const auto *L : Rec.Lambdas)
Diag(L->getLocStart(), 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.
Expand Down Expand Up @@ -15637,7 +15637,8 @@ bool Sema::CheckCallReturnType(QualType ReturnType, SourceLocation Loc,

// If we're inside a decltype's expression, don't check for a valid return
// type or construct temporaries until we know whether this is the last call.
if (ExprEvalContexts.back().IsDecltype) {
if (ExprEvalContexts.back().ExprContext ==
ExpressionEvaluationContextRecord::EK_Decltype) {
ExprEvalContexts.back().DelayedDecltypeCalls.push_back(CE);
return false;
}
Expand Down
10 changes: 7 additions & 3 deletions clang/lib/Sema/SemaExprCXX.cpp
Expand Up @@ -6434,7 +6434,8 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) {
if (RD->isInvalidDecl() || RD->isDependentContext())
return E;

bool IsDecltype = ExprEvalContexts.back().IsDecltype;
bool IsDecltype = ExprEvalContexts.back().ExprContext ==
ExpressionEvaluationContextRecord::EK_Decltype;
CXXDestructorDecl *Destructor = IsDecltype ? nullptr : LookupDestructor(RD);

if (Destructor) {
Expand Down Expand Up @@ -6516,7 +6517,9 @@ Stmt *Sema::MaybeCreateStmtWithCleanups(Stmt *SubStmt) {
/// are omitted for the 'topmost' call in the decltype expression. If the
/// topmost call bound a temporary, strip that temporary off the expression.
ExprResult Sema::ActOnDecltypeExpression(Expr *E) {
assert(ExprEvalContexts.back().IsDecltype && "not in a decltype expression");
assert(ExprEvalContexts.back().ExprContext ==
ExpressionEvaluationContextRecord::EK_Decltype &&
"not in a decltype expression");

// C++11 [expr.call]p11:
// If a function call is a prvalue of object type,
Expand Down Expand Up @@ -6558,7 +6561,8 @@ ExprResult Sema::ActOnDecltypeExpression(Expr *E) {
TopBind = nullptr;

// Disable the special decltype handling now.
ExprEvalContexts.back().IsDecltype = false;
ExprEvalContexts.back().ExprContext =
ExpressionEvaluationContextRecord::EK_Other;

// In MS mode, don't perform any extra checking of call return types within a
// decltype expression.
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/Sema/TreeTransform.h
Expand Up @@ -3859,6 +3859,10 @@ template<typename Derived>
bool TreeTransform<Derived>::TransformTemplateArgument(
const TemplateArgumentLoc &Input,
TemplateArgumentLoc &Output, bool Uneval) {
EnterExpressionEvaluationContext EEEC(
SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated,
/*LambdaContextDecl=*/nullptr, /*ExprContext=*/
Sema::ExpressionEvaluationContextRecord::EK_TemplateArgument);
const TemplateArgument &Arg = Input.getArgument();
switch (Arg.getKind()) {
case TemplateArgument::Null:
Expand Down Expand Up @@ -5494,7 +5498,7 @@ QualType TreeTransform<Derived>::TransformDecltypeType(TypeLocBuilder &TLB,
// decltype expressions are not potentially evaluated contexts
EnterExpressionEvaluationContext Unevaluated(
SemaRef, Sema::ExpressionEvaluationContext::Unevaluated, nullptr,
/*IsDecltype=*/true);
Sema::ExpressionEvaluationContextRecord::EK_Decltype);

ExprResult E = getDerived().TransformExpr(T->getUnderlyingExpr());
if (E.isInvalid())
Expand Down
@@ -0,0 +1,7 @@
// RUN: %clang_cc1 -std=c++17 %s -verify

template<auto> struct Nothing {};

void pr33696() {
Nothing<[]() { return 0; }()> nothing; // expected-error{{a lambda expression cannot appear in this context}}
}

0 comments on commit b6d5c58

Please sign in to comment.