faisalv created this revision.
faisalv added a reviewer: rsmith.
faisalv added a subscriber: cfe-commits.

Mark the lambda's conversion to function-pointer as incontrovertibly constexpr. 

auto L = [](auto a) { return a; };
constexpr int* (*fp)(int*) = L; // This is now allowed.

By itself this is not terribly useful.  A subsequent patch will enable a call 
through the function pointer in a constant expression if the lambda's 
synthesized call operator is constexpr.

The more interesting/controversial aspect of the patch (and the part I really 
need feedback on) is emission of a warning if this is called in pre-c++1z 
constant expressions, and making sure such warnings are ignored when 
determining if the constant expression evaluation succeeded. Look forward to 
your feedback...







http://reviews.llvm.org/D18510

Files:
  include/clang/AST/ASTContext.h
  include/clang/Basic/DiagnosticASTKinds.td
  lib/AST/ASTContext.cpp
  lib/AST/Decl.cpp
  lib/AST/ExprConstant.cpp
  lib/Sema/SemaDecl.cpp
  lib/Sema/SemaDeclAttr.cpp
  lib/Sema/SemaDeclCXX.cpp
  lib/Sema/SemaLambda.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
@@ -2,7 +2,9 @@
 // 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 
+// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -Wc++14-compat %s -DCHECK_COMPATIBILITY_WARNING
 
+#ifndef CHECK_COMPATIBILITY_WARNING
 namespace test_constexpr_checking {
 
 namespace ns1 {
@@ -33,4 +35,17 @@
       L(3); //expected-note{{non-constexpr function}}
 } 
 
-} // end ns test_constexpr_call
\ No newline at end of file
+} // end ns test_constexpr_call
+#endif
+
+#ifdef CHECK_COMPATIBILITY_WARNING
+//expected-warning@+6{{incompatible with C++ standards before C++1z}}
+//expected-warning@+6{{incompatible with C++ standards before C++1z}}
+#endif
+
+namespace ns4 {
+auto L = [](auto a) { return a; };
+constexpr int (*fp1)(int) = L;  
+constexpr int* (*fp2)(int*) = L; 
+
+} // end ns4
\ No newline at end of file
Index: lib/Sema/SemaLambda.cpp
===================================================================
--- lib/Sema/SemaLambda.cpp
+++ lib/Sema/SemaLambda.cpp
@@ -1263,7 +1263,7 @@
                                 ConvTy, 
                                 ConvTSI,
                                 /*isInline=*/true, /*isExplicit=*/false,
-                                /*isConstexpr=*/false, 
+                                /*isConstexpr=*/true, 
                                 CallOperator->getBody()->getLocEnd());
   Conversion->setAccess(AS_public);
   Conversion->setImplicit(true);
Index: lib/Sema/SemaDeclCXX.cpp
===================================================================
--- lib/Sema/SemaDeclCXX.cpp
+++ lib/Sema/SemaDeclCXX.cpp
@@ -1256,11 +1256,11 @@
   if (!Expr::isPotentialConstantExpr(Dcl, Diags)) {
     Diag(Dcl->getLocation(), diag::ext_constexpr_function_never_constant_expr)
       << isa<CXXConstructorDecl>(Dcl);
-    for (size_t I = 0, N = Diags.size(); I != N; ++I)
-      Diag(Diags[I].first, Diags[I].second);
     // Don't return false here: we allow this for compatibility in
     // system headers.
   }
+  for (size_t I = 0, N = Diags.size(); I != N; ++I)
+      Diag(Diags[I].first, Diags[I].second);
 
   return true;
 }
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -819,13 +819,18 @@
     return;
 
   SmallVector<PartialDiagnosticAt, 8> Diags;
-  if (!Cond->isValueDependent() &&
-      !Expr::isPotentialConstantExprUnevaluated(Cond, cast<FunctionDecl>(D),
-                                                Diags)) {
-    S.Diag(Attr.getLoc(), diag::err_enable_if_never_constant_expr);
-    for (int I = 0, N = Diags.size(); I != N; ++I)
-      S.Diag(Diags[I].first, Diags[I].second);
-    return;
+  if (!Cond->isValueDependent()) {
+    if (!Expr::isPotentialConstantExprUnevaluated(Cond, cast<FunctionDecl>(D),
+                                                  Diags)) {
+      S.Diag(Attr.getLoc(), diag::err_enable_if_never_constant_expr);
+      for (int I = 0, N = Diags.size(); I != N; ++I)
+        S.Diag(Diags[I].first, Diags[I].second);
+      return;
+    } else {
+      // Emit any warnings.
+      for (int I = 0, N = Diags.size(); I != N; ++I)
+        S.Diag(Diags[I].first, Diags[I].second);
+    }
   }
 
   D->addAttr(::new (S.Context)
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -10258,9 +10258,10 @@
         }
         Diag(DiagLoc, diag::err_constexpr_var_requires_const_init)
           << var << Init->getSourceRange();
-        for (unsigned I = 0, N = Notes.size(); I != N; ++I)
-          Diag(Notes[I].first, Notes[I].second);
       }
+      // Emit any warnings or notes.
+      for (unsigned I = 0, N = Notes.size(); I != N; ++I)
+        Diag(Notes[I].first, Notes[I].second);
     } else if (var->isUsableInConstantExpressions(Context)) {
       // Check whether the initializer of a const variable of integral or
       // enumeration type is an ICE now, since we can't tell whether it was
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -4237,6 +4237,13 @@
         if (!EvaluateObjectArgument(Info, ME->getBase(), ThisVal))
           return false;
         Member = ME->getMemberDecl();
+        if (const CXXConversionDecl *Conv = dyn_cast<CXXConversionDecl>(Member))
+          if (Conv->getParent()->isLambda()) {
+            Info.Diag(E->getExprLoc(),
+                 !Info.Ctx.getLangOpts().CPlusPlus1z
+                     ? diag::ext_constexpr_conversion_on_lambda_cxx1z
+                     : diag::warn_cxx14_compat_constexpr_conversion_on_lambda);
+          }
         This = &ThisVal;
         HasQualifier = ME->hasQualifier();
       } else if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(Callee)) {
@@ -9622,7 +9629,7 @@
   APValue Scratch;
   bool IsConstExpr = ::EvaluateAsRValue(Info, this, Result ? *Result : Scratch);
 
-  if (!Diags.empty()) {
+  if (!Ctx.isEmptyOrContainsOnlyWarningOrIgnorableDiagnostics(Diags)) {
     IsConstExpr = false;
     if (Loc) *Loc = Diags[0].first;
   } else if (!IsConstExpr) {
@@ -9694,7 +9701,8 @@
     HandleFunctionCall(Loc, FD, (MD && MD->isInstance()) ? &This : nullptr,
                        Args, FD->getBody(), Info, Scratch, nullptr);
 
-  return Diags.empty();
+  return FD->getASTContext().isEmptyOrContainsOnlyWarningOrIgnorableDiagnostics(
+      Diags);
 }
 
 bool Expr::isPotentialConstantExprUnevaluated(Expr *E,
@@ -9718,7 +9726,8 @@
 
   APValue ResultScratch;
   Evaluate(ResultScratch, Info, E);
-  return Diags.empty();
+  return FD->getASTContext().isEmptyOrContainsOnlyWarningOrIgnorableDiagnostics(
+      Diags);
 }
 
 bool Expr::tryEvaluateObjectSize(uint64_t &Result, ASTContext &Ctx,
Index: lib/AST/Decl.cpp
===================================================================
--- lib/AST/Decl.cpp
+++ lib/AST/Decl.cpp
@@ -2185,12 +2185,13 @@
 
   Eval->IsEvaluating = false;
   Eval->WasEvaluated = true;
-
   // In C++11, we have determined whether the initializer was a constant
   // expression as a side-effect.
   if (getASTContext().getLangOpts().CPlusPlus11 && !Eval->CheckedICE) {
     Eval->CheckedICE = true;
-    Eval->IsICE = Result && Notes.empty();
+    Eval->IsICE = Result &&
+        getASTContext().isEmptyOrContainsOnlyWarningOrIgnorableDiagnostics(
+            Notes);
   }
 
   return Result ? &Eval->Evaluated : nullptr;
Index: lib/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp
+++ lib/AST/ASTContext.cpp
@@ -1138,6 +1138,23 @@
   return SourceMgr.getDiagnostics();
 }
 
+bool ASTContext::isEmptyOrContainsOnlyWarningOrIgnorableDiagnostics(
+    ArrayRef<PartialDiagnosticAt> PartialDiags) const {
+  bool OnlyWarnOrIgs = true;
+
+  DiagnosticsEngine &DE = getDiagnostics();
+  for (const PartialDiagnosticAt &PD : PartialDiags) {
+    DiagnosticsEngine::Level DiagLevel =
+        DE.getDiagnosticLevel(PD.second.getDiagID(), PD.first);
+    if (DiagLevel != DiagnosticsEngine::Ignored &&
+        DiagLevel != DiagnosticsEngine::Warning) {
+      OnlyWarnOrIgs = false;
+      break;
+    }
+  }
+  return OnlyWarnOrIgs;
+}
+
 AttrVec& ASTContext::getDeclAttrs(const Decl *D) {
   AttrVec *&Result = DeclAttrs[D];
   if (!Result) {
Index: include/clang/Basic/DiagnosticASTKinds.td
===================================================================
--- include/clang/Basic/DiagnosticASTKinds.td
+++ include/clang/Basic/DiagnosticASTKinds.td
@@ -158,6 +158,15 @@
 // in Sema.
 def note_unimplemented_constexpr_lambda_feature_ast : Note<
     "unimplemented constexpr lambda feature: %0 (coming soon!)">;
+ 
+ // C++1z constexpr lambda expressions
+ def warn_cxx14_compat_constexpr_conversion_on_lambda : Warning<
+    "constexpr conversion to pointer-to-function on lambdas is "
+    "incompatible with C++ standards before C++1z">,
+    InGroup<CXXPre1zCompat>, DefaultIgnore;
+ def ext_constexpr_conversion_on_lambda_cxx1z : ExtWarn<
+    "constexpr conversion to pointer-to-function on lambdas is "
+    "a C++1z extension">, InGroup<CXX1z>;
 
 // inline asm related.
 let CategoryName = "Inline Assembly Issue" in {
Index: include/clang/AST/ASTContext.h
===================================================================
--- include/clang/AST/ASTContext.h
+++ include/clang/AST/ASTContext.h
@@ -578,6 +578,8 @@
   PartialDiagnostic::StorageAllocator &getDiagAllocator() {
     return DiagAllocator;
   }
+  bool isEmptyOrContainsOnlyWarningOrIgnorableDiagnostics(
+      ArrayRef<PartialDiagnosticAt> PartialDiags) const;
 
   const TargetInfo &getTargetInfo() const { return *Target; }
   const TargetInfo *getAuxTargetInfo() const { return AuxTarget; }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to