faisalv created this revision.
faisalv added a reviewer: rsmith.
faisalv added a subscriber: cfe-commits.
faisalv set the repository for this revision to rL LLVM.
faisalv added a project: clang-c.

This patch disables lambda expressions (especially Immediately Invoked Lambda 
Expressions (IILEs, to borrow a term from the all-mighty ecmascript ;)) from 
appearing within potentially mangled contexts.

The approach is a rather primitive/inelegant one.  Instead of enumerating all 
the various syntactic constant-expression contexts at a finer level of 
granularity and then passing that information through to each call of 
ParseConstantExpression, we simply set a flag that forbids lambda expressions 
(when we know we are in the forbidden context).  There is probably an 
opportunity here to streamline the machinery that prohibits lambdas in certain 
contexts across the various versions of C++ further - but that could be 
re-engineered if/when Louis Dionne's paper on Lambdas in Unevaluated contexts 
gets incorporated into the working draft.

Would appreciate some feedback here - thanks!


Repository:
  rL LLVM

https://reviews.llvm.org/D28510

Files:
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Parse/Parser.h
  include/clang/Sema/Sema.h
  lib/Parse/ParseDecl.cpp
  lib/Parse/ParseExpr.cpp
  lib/Parse/ParseTemplate.cpp
  lib/Sema/Sema.cpp
  lib/Sema/SemaExpr.cpp
  test/SemaCXX/cxx1z-constexpr-lambdas.cpp

Index: test/SemaCXX/cxx1z-constexpr-lambdas.cpp
===================================================================
--- test/SemaCXX/cxx1z-constexpr-lambdas.cpp
+++ test/SemaCXX/cxx1z-constexpr-lambdas.cpp
@@ -157,6 +157,38 @@
 
 } // end ns1_simple_lambda
 
+namespace test_forbidden_lambda_expressions {
+
+template<int N = []{ return 5; }()> struct X { }; //expected-error{{lambda expression may not appear}}
+X<[]{return 10; }()> x; //expected-error{{lambda expression may not appear}}
+void f(int arr[([] { return 5; }())]); //expected-error{{lambda expression may not appear}}
+// FIXME: Should this be ok?
+auto L = [](int arr[([] { return 5; }())]) { }; // OK????
+
+// These should be allowed:
+struct A {
+  int : ([] { return 5; }());
+};
+
+int arr[([] { return 5; }())];
+enum { E = [] { return 5; }() };
+static_assert([]{return 5; }() == 5);
+int *ip = new int[([] { return 5; })()];
+
+int test_case(int x) {
+  switch(x) {
+    case [] { return 5; }(): //OK
+      break;
+    case [] (auto a) { return a; }(6): //expected-note{{previous}}
+      break;
+    case 6:  //expected-error{{duplicate}}
+      break;
+  }
+  return x;
+}
+
+} // end ns forbidden_lambda_expressions
+
 namespace ns1_unimplemented {
 namespace ns1_captures {
 constexpr auto f(int i) {
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -13083,9 +13083,11 @@
 void
 Sema::PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
                                       Decl *LambdaContextDecl,
-                                      bool IsDecltype) {
+                                      bool IsDecltype,
+                                      bool IsLambdaExprForbidden) {
   ExprEvalContexts.emplace_back(NewContext, ExprCleanupObjects.size(), Cleanup,
-                                LambdaContextDecl, IsDecltype);
+                                LambdaContextDecl, IsDecltype,
+                                IsLambdaExprForbidden);
   Cleanup.reset();
   if (!MaybeODRUseExprs.empty())
     std::swap(MaybeODRUseExprs, ExprEvalContexts.back().SavedMaybeODRUseExprs);
@@ -13094,9 +13096,11 @@
 void
 Sema::PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
                                       ReuseLambdaContextDecl_t,
-                                      bool IsDecltype) {
+                                      bool IsDecltype,
+                                      bool IsLambdaExprForbidden) {
   Decl *ClosureContextDecl = ExprEvalContexts.back().ManglingContextDecl;
-  PushExpressionEvaluationContext(NewContext, ClosureContextDecl, IsDecltype);
+  PushExpressionEvaluationContext(NewContext, ClosureContextDecl, IsDecltype,
+                                  IsLambdaExprForbidden);
 }
 
 void Sema::PopExpressionEvaluationContext() {
@@ -13117,6 +13121,8 @@
         //   evaluation of e, following the rules of the abstract machine, would
         //   evaluate [...] a lambda-expression.
         D = diag::err_lambda_in_constant_expression;
+        if (getLangOpts().CPlusPlus1z && Rec.IsLambdaExprForbidden)
+          D = diag::err_lambda_in_potentially_mangled_constant_expression;
       }
 
       // C++1z allows lambda expressions as core constant expressions.
@@ -13125,9 +13131,11 @@
       // 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.Context != ConstantEvaluated || !getLangOpts().CPlusPlus1z)
+      if (Rec.Context != ConstantEvaluated || Rec.IsLambdaExprForbidden ||
+          !getLangOpts().CPlusPlus1z) {
         for (const auto *L : Rec.Lambdas)
           Diag(L->getLocStart(), D);
+      }
     } else {
       // Mark the capture expressions odr-used. This was deferred
       // during lambda expression creation.
Index: lib/Sema/Sema.cpp
===================================================================
--- lib/Sema/Sema.cpp
+++ lib/Sema/Sema.cpp
@@ -123,7 +123,7 @@
   Diags.SetArgToStringFn(&FormatASTNodeDiagnosticArgument, &Context);
 
   ExprEvalContexts.emplace_back(PotentiallyEvaluated, 0, CleanupInfo{}, nullptr,
-                                false);
+                                false, /*IsLambdaExprForbidden*/false);
 
   FunctionScopes.push_back(new FunctionScopeInfo(Diags));
 
Index: lib/Parse/ParseTemplate.cpp
===================================================================
--- lib/Parse/ParseTemplate.cpp
+++ lib/Parse/ParseTemplate.cpp
@@ -701,8 +701,12 @@
     //   end of the template-parameter-list rather than a greater-than
     //   operator.
     GreaterThanIsOperatorScope G(GreaterThanIsOperator, false);
-    EnterExpressionEvaluationContext ConstantEvaluated(Actions,
-                                                       Sema::ConstantEvaluated);
+    EnterExpressionEvaluationContext ConstantEvaluated(
+        Actions, Sema::ConstantEvaluated,
+        /*LambdaContextDecl=*/nullptr,
+        /*IsDecltype=*/false,
+        /*ShouldEnter=*/true,
+        /*IsLambdaExprForbidden*/true);
 
     DefaultArg = Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression());
     if (DefaultArg.isInvalid())
@@ -1220,7 +1224,8 @@
   
   // Parse a non-type template argument. 
   SourceLocation Loc = Tok.getLocation();
-  ExprResult ExprArg = ParseConstantExpression(MaybeTypeCast);
+  ExprResult ExprArg = ParseConstantExpression(MaybeTypeCast,
+                                               /*IsLambdaExprForbidden*/true);
   if (ExprArg.isInvalid() || !ExprArg.get())
     return ParsedTemplateArgument();
 
Index: lib/Parse/ParseExpr.cpp
===================================================================
--- lib/Parse/ParseExpr.cpp
+++ lib/Parse/ParseExpr.cpp
@@ -193,13 +193,18 @@
 }
 
 
-ExprResult Parser::ParseConstantExpression(TypeCastState isTypeCast) {
+ExprResult Parser::ParseConstantExpression(TypeCastState isTypeCast,
+                                           bool IsLambdaExprForbidden) {
   // C++03 [basic.def.odr]p2:
   //   An expression is potentially evaluated unless it appears where an
   //   integral constant expression is required (see 5.19) [...].
   // C++98 and C++11 have no such rule, but this is only a defect in C++98.
-  EnterExpressionEvaluationContext ConstantEvaluated(Actions,
-                                               Sema::ConstantEvaluated);
+  EnterExpressionEvaluationContext ConstantEvaluated(
+      Actions, Sema::ConstantEvaluated,
+      /*LambdaContextDecl=*/nullptr,
+      /*IsDecltype=*/false,
+      /*ShouldEnter=*/true,
+      /*IsLambdaExprForbidden=*/IsLambdaExprForbidden);
 
   ExprResult LHS(ParseCastExpression(false, false, isTypeCast));
   ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::Conditional));
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp
+++ lib/Parse/ParseDecl.cpp
@@ -6250,7 +6250,8 @@
     // Parse the constant-expression or assignment-expression now (depending
     // on dialect).
     if (getLangOpts().CPlusPlus) {
-      NumElements = ParseConstantExpression();
+      NumElements = ParseConstantExpression(NotTypeCast,
+       /*IsLambdaExprForbidden=*/D.getContext() == D.PrototypeContext);
     } else {
       EnterExpressionEvaluationContext Unevaluated(Actions,
                                                    Sema::ConstantEvaluated);
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -891,15 +891,20 @@
     /// expressions for which we have deferred checking the destructor.
     SmallVector<CXXBindTemporaryExpr *, 8> DelayedDecltypeBinds;
 
+    /// \brief Whether lambda expressions are forbidden here.
+    bool IsLambdaExprForbidden;
+
     ExpressionEvaluationContextRecord(ExpressionEvaluationContext Context,
                                       unsigned NumCleanupObjects,
                                       CleanupInfo ParentCleanup,
                                       Decl *ManglingContextDecl,
-                                      bool IsDecltype)
+                                      bool IsDecltype,
+                                      bool IsLambdaExprForbidden)
       : Context(Context), ParentCleanup(ParentCleanup),
         IsDecltype(IsDecltype), NumCleanupObjects(NumCleanupObjects),
         NumTypos(0),
-        ManglingContextDecl(ManglingContextDecl), MangleNumbering() { }
+        ManglingContextDecl(ManglingContextDecl), MangleNumbering(),
+        IsLambdaExprForbidden(IsLambdaExprForbidden) { }
 
     /// \brief Retrieve the mangling numbering context, used to consistently
     /// number constructs like lambdas for mangling.
@@ -3764,11 +3769,13 @@
 
   void PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
                                        Decl *LambdaContextDecl = nullptr,
-                                       bool IsDecltype = false);
+                                       bool IsDecltype = false,
+                                       bool IsLambdaExprForbidden = false);
   enum ReuseLambdaContextDecl_t { ReuseLambdaContextDecl };
   void PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
                                        ReuseLambdaContextDecl_t,
-                                       bool IsDecltype = false);
+                                       bool IsDecltype = false,
+                                       bool IsLambdaExprForbidden = false);
   void PopExpressionEvaluationContext();
 
   void DiscardCleanupsInEvaluationContext();
@@ -10232,11 +10239,13 @@
                                    Sema::ExpressionEvaluationContext NewContext,
                                    Decl *LambdaContextDecl = nullptr,
                                    bool IsDecltype = false,
-                                   bool ShouldEnter = true)
+                                   bool ShouldEnter = true,
+                                   bool IsLambdaExprForbidden = false)
       : Actions(Actions), Entered(ShouldEnter) {
     if (Entered)
       Actions.PushExpressionEvaluationContext(NewContext, LambdaContextDecl,
-                                              IsDecltype);
+                                              IsDecltype, 
+                                              IsLambdaExprForbidden);
   }
   EnterExpressionEvaluationContext(Sema &Actions,
                                    Sema::ExpressionEvaluationContext NewContext,
Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h
+++ include/clang/Parse/Parser.h
@@ -1428,7 +1428,8 @@
   };
 
   ExprResult ParseExpression(TypeCastState isTypeCast = NotTypeCast);
-  ExprResult ParseConstantExpression(TypeCastState isTypeCast = NotTypeCast);
+  ExprResult ParseConstantExpression(TypeCastState isTypeCast = NotTypeCast,
+                                     bool IsLambdaExprForbidden = false);
   ExprResult ParseConstraintExpression();
   // Expr that doesn't include commas.
   ExprResult ParseAssignmentExpression(TypeCastState isTypeCast = NotTypeCast);
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -6268,6 +6268,8 @@
     "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_potentially_mangled_constant_expression : Error<
+    "a lambda expression may not appear inside of a template argument or in a function signature">;
   def err_lambda_return_init_list : Error<
     "cannot deduce lambda return type from initializer list">;
   def err_lambda_capture_default_arg : Error<
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to