ahatanak created this revision.
ahatanak added a subscriber: cfe-commits.

clang currently errors out when a lambda expression is used to compute the size 
of an array even though clang supports variable-length arrays as a C99 
extension. For example,

$ cat vla1.cpp

```
int foo3();

struct S1 {
  virtual ~S1();
};

struct S2 : S1 {
};

void foo(S1 *s1) {
  unsigned char a1[([](int a) {return a; })(1)];
  unsigned char a2[foo3()]; // This is OK.
  unsigned char a3[!dynamic_cast<S2 *>(s1) + 1];  // This is OK.
}
```

$ clang++ vla1.cpp -c -std=c++11
vla1.cpp:11:21: error: a lambda expression may not appear inside of a constant 
expression
  unsigned char a1[([](int a) {return a; })(1)];
                    ^
1 error generated.

To handle VLAs in a more consistent way, this patch makes changes to allow 
lambda expressions to be used for array bounds if it is in a BlockContext.

http://reviews.llvm.org/D21187

Files:
  include/clang/Parse/Parser.h
  include/clang/Sema/Sema.h
  lib/Parse/ParseDecl.cpp
  lib/Parse/ParseExpr.cpp
  lib/Sema/Sema.cpp
  lib/Sema/SemaExpr.cpp
  test/SemaCXX/lambda-expressions.cpp

Index: test/SemaCXX/lambda-expressions.cpp
===================================================================
--- test/SemaCXX/lambda-expressions.cpp
+++ test/SemaCXX/lambda-expressions.cpp
@@ -499,3 +499,12 @@
   };
 }
 }
+
+namespace array_bound {
+void foo() {
+  int a0[([](){ return 4; })()];
+  int a2[([](int n){ return n; })(4)];
+}
+
+int a2[([](){ return 4; })()]; // expected-error {{a lambda expression may not appear inside of a constant expression}} expected-error {{variable length array declaration not allowed at file scope}}
+}
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -12791,10 +12791,10 @@
 void
 Sema::PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
                                       Decl *LambdaContextDecl,
-                                      bool IsDecltype) {
+                                      bool IsDecltype, bool IsArrayBound) {
   ExprEvalContexts.emplace_back(NewContext, ExprCleanupObjects.size(),
                                 ExprNeedsCleanups, LambdaContextDecl,
-                                IsDecltype);
+                                IsDecltype, IsArrayBound);
   ExprNeedsCleanups = false;
   if (!MaybeODRUseExprs.empty())
     std::swap(MaybeODRUseExprs, ExprEvalContexts.back().SavedMaybeODRUseExprs);
@@ -12814,21 +12814,22 @@
 
   if (!Rec.Lambdas.empty()) {
     if (Rec.isUnevaluated() || Rec.Context == ConstantEvaluated) {
-      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 {
+        for (const auto *L : Rec.Lambdas)
+          Diag(L->getLocStart(), diag::err_lambda_unevaluated_operand);
+      // Don't error out on lambdas used to compute array size in a
+      // BlockContext.
+      } else if (!Rec.IsArrayBound) {
         // 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;
+        for (const auto *L : Rec.Lambdas)
+          Diag(L->getLocStart(), diag::err_lambda_in_constant_expression);
       }
-      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
@@ -124,7 +124,8 @@
   // Tell diagnostics how to render things from the AST library.
   Diags.SetArgToStringFn(&FormatASTNodeDiagnosticArgument, &Context);
 
-  ExprEvalContexts.emplace_back(PotentiallyEvaluated, 0, false, nullptr, false);
+  ExprEvalContexts.emplace_back(PotentiallyEvaluated, 0, false, nullptr, false,
+                                false);
 
   FunctionScopes.push_back(new FunctionScopeInfo(Diags));
 
Index: lib/Parse/ParseExpr.cpp
===================================================================
--- lib/Parse/ParseExpr.cpp
+++ lib/Parse/ParseExpr.cpp
@@ -194,13 +194,14 @@
 }
 
 
-ExprResult Parser::ParseConstantExpression(TypeCastState isTypeCast) {
+ExprResult Parser::ParseConstantExpression(TypeCastState isTypeCast,
+                                           bool IsArrayBound) {
   // 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 Unevaluated(Actions,
-                                               Sema::ConstantEvaluated);
+  EnterExpressionEvaluationContext Unevaluated(Actions, Sema::ConstantEvaluated,
+                                               nullptr, false, IsArrayBound);
 
   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
@@ -6103,7 +6103,8 @@
     // Parse the constant-expression or assignment-expression now (depending
     // on dialect).
     if (getLangOpts().CPlusPlus) {
-      NumElements = ParseConstantExpression();
+      NumElements = ParseConstantExpression(
+          Parser::NotTypeCast, D.getContext() == Declarator::BlockContext);
     } else {
       EnterExpressionEvaluationContext Unevaluated(Actions,
                                                    Sema::ConstantEvaluated);
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -854,6 +854,9 @@
     /// suffice, e.g., in a default function argument.
     Decl *ManglingContextDecl;
 
+    /// \brief Whether an array bound in a BlockContext is being parsed.
+    bool IsArrayBound;
+
     /// \brief The context information used to mangle lambda expressions
     /// and block literals within this context.
     ///
@@ -873,11 +876,12 @@
                                       unsigned NumCleanupObjects,
                                       bool ParentNeedsCleanups,
                                       Decl *ManglingContextDecl,
-                                      bool IsDecltype)
+                                      bool IsDecltype, bool IsArrayBound)
       : Context(Context), ParentNeedsCleanups(ParentNeedsCleanups),
         IsDecltype(IsDecltype), NumCleanupObjects(NumCleanupObjects),
         NumTypos(0),
-        ManglingContextDecl(ManglingContextDecl), MangleNumbering() { }
+        ManglingContextDecl(ManglingContextDecl),
+        IsArrayBound(IsArrayBound), MangleNumbering() {}
 
     /// \brief Retrieve the mangling numbering context, used to consistently
     /// number constructs like lambdas for mangling.
@@ -3639,7 +3643,8 @@
 
   void PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
                                        Decl *LambdaContextDecl = nullptr,
-                                       bool IsDecltype = false);
+                                       bool IsDecltype = false,
+                                       bool IsArrayBound = false);
   enum ReuseLambdaContextDecl_t { ReuseLambdaContextDecl };
   void PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
                                        ReuseLambdaContextDecl_t,
@@ -9494,10 +9499,11 @@
   EnterExpressionEvaluationContext(Sema &Actions,
                                    Sema::ExpressionEvaluationContext NewContext,
                                    Decl *LambdaContextDecl = nullptr,
-                                   bool IsDecltype = false)
+                                   bool IsDecltype = false,
+                                   bool IsArrayBound = false)
     : Actions(Actions) {
     Actions.PushExpressionEvaluationContext(NewContext, LambdaContextDecl,
-                                            IsDecltype);
+                                            IsDecltype, IsArrayBound);
   }
   EnterExpressionEvaluationContext(Sema &Actions,
                                    Sema::ExpressionEvaluationContext NewContext,
Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h
+++ include/clang/Parse/Parser.h
@@ -1396,7 +1396,8 @@
   };
 
   ExprResult ParseExpression(TypeCastState isTypeCast = NotTypeCast);
-  ExprResult ParseConstantExpression(TypeCastState isTypeCast = NotTypeCast);
+  ExprResult ParseConstantExpression(TypeCastState isTypeCast = NotTypeCast,
+                                     bool IsArrayBound = false);
   ExprResult ParseConstraintExpression();
   // Expr that doesn't include commas.
   ExprResult ParseAssignmentExpression(TypeCastState isTypeCast = NotTypeCast);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to