From: Douglas Gregor Date: Thu, 9 Feb 2012 08:14:43 +0000 (+0000) Subject: Implement C++ [expr.prim.lambda]p2, which bans lambda expressions in X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e2c5913c48f66bfec9e58a8ad1d90e5eeffad586;p=clang Implement C++ [expr.prim.lambda]p2, which bans lambda expressions in unevaluated operands. Be certain that we're marking everything referenced within a capture initializer as odr-used. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150163 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index cbb4d177ef..c8b783cb4f 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -4125,7 +4125,8 @@ def err_lambda_impcap : Error< "variable %0 cannot be implicitly captured in a lambda with no " "capture-default specified">; def note_lambda_decl : Note<"lambda expression begins here">; - +def err_lambda_unevaluated_operand : Error< + "lambda expression in an unevaluated operand">; def err_operator_arrow_circular : Error< "circular pointer delegation detected">; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 6a0ffa9b30..fd9292339e 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -102,6 +102,7 @@ namespace clang { class InitializedEntity; class IntegerLiteral; class LabelStmt; + class LambdaExpr; class LangOptions; class LocalInstantiationScope; class LookupResult; @@ -560,6 +561,10 @@ public: llvm::SmallPtrSet SavedMaybeODRUseExprs; + /// \brief The lambdas that are present within this context, if it + /// is indeed an unevaluated context. + llvm::SmallVector Lambdas; + ExpressionEvaluationContextRecord(ExpressionEvaluationContext Context, unsigned NumCleanupObjects, bool ParentNeedsCleanups) diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 36bbae03f8..de70ad4cba 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -9282,6 +9282,12 @@ namespace { return BaseTransform::TransformUnaryOperator(E); } + /// \brief Transform the capture expressions in the lambda + /// expression. + ExprResult TransformLambdaExpr(LambdaExpr *E) { + // Lambdas never need to be transformed. + return E; + } }; } @@ -9307,6 +9313,29 @@ Sema::PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext) { void Sema::PopExpressionEvaluationContext() { ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back(); + if (!Rec.Lambdas.empty()) { + if (Rec.Context == Unevaluated) { + // C++11 [expr.prim.lambda]p2: + // A lambda-expression shall not appear in an unevaluated operand + // (Clause 5). + for (unsigned I = 0, N = Rec.Lambdas.size(); I != N; ++I) + Diag(Rec.Lambdas[I]->getLocStart(), + diag::err_lambda_unevaluated_operand); + } else { + // Mark the capture expressions odr-used. This was deferred + // during lambda expression creation. + for (unsigned I = 0, N = Rec.Lambdas.size(); I != N; ++I) { + LambdaExpr *Lambda = Rec.Lambdas[I]; + for (LambdaExpr::capture_init_iterator + C = Lambda->capture_init_begin(), + CEnd = Lambda->capture_init_end(); + C != CEnd; ++C) { + MarkDeclarationsReferencedInExpr(*C); + } + } + } + } + // When are coming out of an unevaluated context, clear out any // temporaries that we may have created as part of the evaluation of // the expression in that context: they aren't relevant because they @@ -9318,6 +9347,8 @@ void Sema::PopExpressionEvaluationContext() { CleanupVarDeclMarking(); std::swap(MaybeODRUseExprs, Rec.SavedMaybeODRUseExprs); + if (Rec.Context == Unevaluated) { + } // Otherwise, merge the contexts together. } else { ExprNeedsCleanups |= Rec.ParentNeedsCleanups; @@ -9589,8 +9620,9 @@ static ExprResult captureInLambda(Sema &S, LambdaScopeInfo *LSI, // // FIXME: Introduce an initialization entity for lambda captures. - // Introduce a new evaluation context for the initialization, so that - // temporaries introduced as part of the capture + // Introduce a new evaluation context for the initialization, so + // that temporaries introduced as part of the capture are retained + // to be re-"exported" from the lambda expression itself. S.PushExpressionEvaluationContext(Sema::PotentiallyEvaluated); Expr *Ref = new (S.Context) DeclRefExpr(Var, Type.getNonReferenceType(), diff --git a/lib/Sema/SemaLambda.cpp b/lib/Sema/SemaLambda.cpp index 776d27a364..afc4d5de27 100644 --- a/lib/Sema/SemaLambda.cpp +++ b/lib/Sema/SemaLambda.cpp @@ -357,9 +357,28 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, if (LambdaExprNeedsCleanups) ExprNeedsCleanups = true; - Expr *Lambda = LambdaExpr::Create(Context, Class, IntroducerRange, - CaptureDefault, Captures, ExplicitParams, - CaptureInits, Body->getLocEnd()); + LambdaExpr *Lambda = LambdaExpr::Create(Context, Class, IntroducerRange, + CaptureDefault, Captures, + ExplicitParams, CaptureInits, + Body->getLocEnd()); + + // C++11 [expr.prim.lambda]p2: + // A lambda-expression shall not appear in an unevaluated operand + // (Clause 5). + switch (ExprEvalContexts.back().Context) { + case Unevaluated: + // 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). + ExprEvalContexts.back().Lambdas.push_back(Lambda); + break; + + case ConstantEvaluated: + case PotentiallyEvaluated: + case PotentiallyEvaluatedIfUsed: + break; + } + Diag(StartLoc, diag::err_lambda_unsupported); return MaybeBindToTemporary(Lambda); diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p2.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p2.cpp new file mode 100644 index 0000000000..48c00aa7c6 --- /dev/null +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p2.cpp @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++11 %s -verify + +// prvalue +void prvalue() { + auto&& x = []()->void { }; // expected-error{{lambda expressions are not supported yet}} + auto& y = []()->void { }; // expected-error{{cannot bind to a temporary of type}} \ + // expected-error{{lambda expressions are not supported yet}} +} + +namespace std { + class type_info; +} + +struct P { + virtual ~P(); +}; + +void unevaluated_operand(P &p, int i) { + int i2 = sizeof([]()->void{}()); // expected-error{{lambda expression in an unevaluated operand}} \ + // expected-error{{lambda expressions are not supported yet}} + const std::type_info &ti1 = typeid([&]() -> P& { return p; }()); // expected-error{{lambda expressions are not supported yet}} + const std::type_info &ti2 = typeid([&]() -> int { return i; }()); // expected-error{{lambda expression in an unevaluated operand}} \ + // expected-error{{lambda expressions are not supported yet}} +} + +template +struct Boom { + Boom(const Boom&) { + T* x = 1; // expected-error{{cannot initialize a variable of type 'int *' with an rvalue of type 'int'}} \ + // expected-error{{cannot initialize a variable of type 'float *' with an rvalue of type 'int'}} \ + // expected-error{{cannot initialize a variable of type 'double *' with an rvalue of type 'int'}} + } + void tickle() const; +}; + +void odr_used(P &p, Boom boom_int, Boom boom_float, + Boom boom_double) { + const std::type_info &ti1 + = typeid([=,&p]() -> P& { boom_int.tickle(); return p; }()); // expected-error{{lambda expressions are not supported yet}} \ + // expected-note{{in instantiation of member function 'Boom::Boom' requested here}} + const std::type_info &ti2 + = typeid([=]() -> int { boom_float.tickle(); return 0; }()); // expected-error{{lambda expression in an unevaluated operand}} \ + // expected-error{{lambda expressions are not supported yet}} \ + // expected-note{{in instantiation of member function 'Boom::Boom' requested here}} + + auto foo = [=]() -> int { boom_double.tickle(); return 0; }; // expected-error{{lambda expressions are not supported yet}} \ + // expected-note{{in instantiation of member function 'Boom::Boom' requested here}} +}