Skip to content

Commit

Permalink
[cxx1z-constexpr-lambda] Make conversion function constexpr, and teac…
Browse files Browse the repository at this point in the history
…h 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!

llvm-svn: 291397
  • Loading branch information
faisalv committed Jan 8, 2017
1 parent ccd3e68 commit d92e749
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 7 deletions.
46 changes: 40 additions & 6 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4543,6 +4543,12 @@ class ExprEvaluatorBase
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.
Expand All @@ -4558,14 +4564,42 @@ class ExprEvaluatorBase
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");

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;
}

// 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);
}

} else
return Error(E);

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaLambda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1274,7 +1274,7 @@ static void addFunctionPointerConversion(Sema &S,
ConvTy,
ConvTSI,
/*isInline=*/true, /*isExplicit=*/false,
/*isConstexpr=*/false,
/*isConstexpr=*/S.getLangOpts().CPlusPlus1z,
CallOperator->getBody()->getLocEnd());
Conversion->setAccess(AS_public);
Conversion->setImplicit(true);
Expand Down
37 changes: 37 additions & 0 deletions clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,41 @@ void f(char c) { //expected-note{{declared here}}
}

}

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

0 comments on commit d92e749

Please sign in to comment.