r291397 - [cxx1z-constexpr-lambda] Make conversion function constexpr, and teach the expression-evaluator to evaluate the static-invoker.

Faisal Vali via cfe-commits cfe-commits at lists.llvm.org
Sun Jan 8 10:56:11 PST 2017


Author: faisalv
Date: Sun Jan  8 12:56:11 2017
New Revision: 291397

URL: http://llvm.org/viewvc/llvm-project?rev=291397&view=rev
Log:
[cxx1z-constexpr-lambda] Make conversion function constexpr, and teach the expression-evaluator to evaluate the static-invoker.

This patch has been sitting in review hell since july 2016 and our lack of constexpr lambda support is getting embarrassing (given that I've had a branch that implements the feature (modulo *this capture) for over a year.  While in Issaquah I was enjoying shamelessly trying to convince folks of the lie that this was Richard's fault ;) I won't be able to do so in Kona since I won't be attending - so I'm going to aim to have this feature be implemented by then.

I'm quite confident of the approach in this patch, which simply maps the static-invoker 'thunk' back to the corresponding call-operator (specialization).

Thanks!


Modified:
    cfe/trunk/lib/AST/ExprConstant.cpp
    cfe/trunk/lib/Sema/SemaLambda.cpp
    cfe/trunk/test/SemaCXX/cxx1z-constexpr-lambdas.cpp

Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=291397&r1=291396&r2=291397&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Sun Jan  8 12:56:11 2017
@@ -4543,6 +4543,12 @@ public:
                              Call.getLValueBase().dyn_cast<const ValueDecl*>());
       if (!FD)
         return Error(Callee);
+      // Don't call function pointers which have been cast to some other type.
+      // Per DR (no number yet), the caller and callee can differ in noexcept.
+      if (!Info.Ctx.hasSameFunctionTypeIgnoringExceptionSpec(
+        CalleeType->getPointeeType(), FD->getType())) {
+        return Error(E);
+      }
 
       // Overloaded operator calls to member functions are represented as normal
       // calls with '*this' as the first argument.
@@ -4558,14 +4564,42 @@ public:
           return false;
         This = &ThisVal;
         Args = Args.slice(1);
-      }
+      } else if (MD && MD->isLambdaStaticInvoker()) {   
+        // Map the static invoker for the lambda back to the call operator.
+        // Conveniently, we don't have to slice out the 'this' argument (as is
+        // being done for the non-static case), since a static member function
+        // doesn't have an implicit argument passed in.
+        const CXXRecordDecl *ClosureClass = MD->getParent();
+        assert(
+            ClosureClass->captures_begin() == ClosureClass->captures_end() &&
+            "Number of captures must be zero for conversion to function-ptr");
 
-      // Don't call function pointers which have been cast to some other type.
-      // Per DR (no number yet), the caller and callee can differ in noexcept.
-      if (!Info.Ctx.hasSameFunctionTypeIgnoringExceptionSpec(
-              CalleeType->getPointeeType(), FD->getType())) {
-        return Error(E);
+        const CXXMethodDecl *LambdaCallOp =
+            ClosureClass->getLambdaCallOperator();
+
+        // Set 'FD', the function that will be called below, to the call
+        // operator.  If the closure object represents a generic lambda, find
+        // the corresponding specialization of the call operator.
+
+        if (ClosureClass->isGenericLambda()) {
+          assert(MD->isFunctionTemplateSpecialization() &&
+                 "A generic lambda's static-invoker function must be a "
+                 "template specialization");
+          const TemplateArgumentList *TAL = MD->getTemplateSpecializationArgs();
+          FunctionTemplateDecl *CallOpTemplate =
+              LambdaCallOp->getDescribedFunctionTemplate();
+          void *InsertPos = nullptr;
+          FunctionDecl *CorrespondingCallOpSpecialization =
+              CallOpTemplate->findSpecialization(TAL->asArray(), InsertPos);
+          assert(CorrespondingCallOpSpecialization &&
+                 "We must always have a function call operator specialization "
+                 "that corresponds to our static invoker specialization");
+          FD = cast<CXXMethodDecl>(CorrespondingCallOpSpecialization);
+        } else
+          FD = LambdaCallOp;
       }
+
+      
     } else
       return Error(E);
 

Modified: cfe/trunk/lib/Sema/SemaLambda.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLambda.cpp?rev=291397&r1=291396&r2=291397&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaLambda.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLambda.cpp Sun Jan  8 12:56:11 2017
@@ -1274,7 +1274,7 @@ static void addFunctionPointerConversion
                                 ConvTy, 
                                 ConvTSI,
                                 /*isInline=*/true, /*isExplicit=*/false,
-                                /*isConstexpr=*/false, 
+                                /*isConstexpr=*/S.getLangOpts().CPlusPlus1z, 
                                 CallOperator->getBody()->getLocEnd());
   Conversion->setAccess(AS_public);
   Conversion->setImplicit(true);

Modified: cfe/trunk/test/SemaCXX/cxx1z-constexpr-lambdas.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx1z-constexpr-lambdas.cpp?rev=291397&r1=291396&r2=291397&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx1z-constexpr-lambdas.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx1z-constexpr-lambdas.cpp Sun Jan  8 12:56:11 2017
@@ -59,4 +59,41 @@ void f(char c) { //expected-note{{declar
 }
 
 }
+
+namespace test_conversion_function_for_non_capturing_lambdas {
+
+namespace ns1 {
+auto L = [](int i) { return i; };
+constexpr int (*fpi)(int) = L;
+static_assert(fpi(3) == 3);
+auto GL = [](auto a) { return a; };
+
+constexpr char (*fp2)(char) = GL;
+constexpr double (*fp3)(double) = GL;
+constexpr const char* (*fp4)(const char*) = GL;
+static_assert(fp2('3') == '3');
+static_assert(fp3(3.14) == 3.14);
+constexpr const char *Str = "abc";
+static_assert(fp4(Str) == Str);
+
+auto NCL = [](int i) { static int j; return j; }; //expected-note{{declared here}}
+constexpr int (*fp5)(int) = NCL;
+constexpr int I =  //expected-error{{must be initialized by a constant expression}}
+                  fp5(5); //expected-note{{non-constexpr function}} 
+
+namespace test_dont_always_instantiate_constexpr_templates {
+
+auto explicit_return_type = [](auto x) -> int { return x.get(); };
+decltype(explicit_return_type(0)) c;  // OK
+
+auto deduced_return_type = [](auto x) { return x.get(); }; //expected-error{{not a structure or union}}
+decltype(deduced_return_type(0)) d;  //expected-note{{requested here}}
+
+
+  
+} // end ns test_dont_always_instantiate_constexpr_templates
+} // end ns1
+
+} // end ns test_conversion_function_for_non_capturing_lambdas
+
 #endif // ndef CPP14_AND_EARLIER




More information about the cfe-commits mailing list