erik.pilkington created this revision. erik.pilkington added reviewers: rsmith, faisalv.
Previously, this caused ExprConstant to assert while verifying the lambda is constexpr: void f() { int x = 0; [=]() constexpr { return x; }; } The problem is that ActOnFinishFunctionBody evaluated the lambda's body before BuildLambdaExpr finished the lambda class. This patch fixes the problem by moving that check to BuildLambdaExpr. PR36054 Thanks for taking a look! Erik Repository: rC Clang https://reviews.llvm.org/D45194 Files: clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaLambda.cpp clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp Index: clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp =================================================================== --- clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp +++ clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp @@ -270,4 +270,37 @@ } // end ns test_lambda_is_cce +namespace PR36054 { +constexpr int fn() { + int Capture = 42; + return [=]() constexpr { return Capture; }(); +} + +static_assert(fn() == 42, ""); + +template <class T> +constexpr int tfn() { + int Capture = 42; + return [=]() constexpr { return Capture; }(); +} + +static_assert(tfn<int>() == 42, ""); + +constexpr int gfn() { + int Capture = 42; + return [=](auto P) constexpr { return Capture + P; }(58); +} + +static_assert(gfn() == 100, ""); + +constexpr bool OtherCaptures() { + int Capture = 42; + constexpr auto Outer = [](auto P) constexpr { return 42 + P; }; + auto Inner = [&](auto O) constexpr { return O(58) + Capture; }; + return Inner(Outer) == 142; +} + +static_assert(OtherCaptures(), ""); +} // namespace PR36054 + #endif // ndef CPP14_AND_EARLIER Index: clang/lib/Sema/SemaLambda.cpp =================================================================== --- clang/lib/Sema/SemaLambda.cpp +++ clang/lib/Sema/SemaLambda.cpp @@ -1604,17 +1604,27 @@ ExplicitParams, ExplicitResultType, CaptureInits, EndLoc, ContainsUnexpandedParameterPack); - // If the lambda expression's call operator is not explicitly marked constexpr - // and we are not in a dependent context, analyze the call operator to infer - // its constexpr-ness, suppressing diagnostics while doing so. - if (getLangOpts().CPlusPlus17 && !CallOperator->isInvalidDecl() && - !CallOperator->isConstexpr() && - !isa<CoroutineBodyStmt>(CallOperator->getBody()) && - !Class->getDeclContext()->isDependentContext()) { - TentativeAnalysisScope DiagnosticScopeGuard(*this); - CallOperator->setConstexpr( - CheckConstexprFunctionDecl(CallOperator) && - CheckConstexprFunctionBody(CallOperator, CallOperator->getBody())); + + if (!CallOperator->isInvalidDecl()) { + // If the call operator is explicitly marked constexpr, verify that that is + // actually the case. This is done here instead of in + // ActOnFinishFunctionBody because the constexpr evaluator needs access to + // the completed lambda class to check this. + if (CallOperator->isConstexpr()) { + if (!CheckConstexprFunctionDecl(CallOperator) || + !CheckConstexprFunctionBody(CallOperator, CallOperator->getBody())) + CallOperator->setInvalidDecl(); + } + // Otherwise, if we're not in a dependent context, analyze the call operator + // to infer its constexpr-ness, suppressing diagnostics while doing so. + else if (getLangOpts().CPlusPlus17 && + !isa<CoroutineBodyStmt>(CallOperator->getBody()) && + !Class->getDeclContext()->isDependentContext()) { + TentativeAnalysisScope DiagnosticScopeGuard(*this); + CallOperator->setConstexpr( + CheckConstexprFunctionDecl(CallOperator) && + CheckConstexprFunctionBody(CallOperator, CallOperator->getBody())); + } } // Emit delayed shadowing warnings now that the full capture list is known. Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -12883,6 +12883,7 @@ } if (!IsInstantiation && FD && FD->isConstexpr() && !FD->isInvalidDecl() && + !isLambdaCallOperator(FD) && (!CheckConstexprFunctionDecl(FD) || !CheckConstexprFunctionBody(FD, Body))) FD->setInvalidDecl();
Index: clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp =================================================================== --- clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp +++ clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp @@ -270,4 +270,37 @@ } // end ns test_lambda_is_cce +namespace PR36054 { +constexpr int fn() { + int Capture = 42; + return [=]() constexpr { return Capture; }(); +} + +static_assert(fn() == 42, ""); + +template <class T> +constexpr int tfn() { + int Capture = 42; + return [=]() constexpr { return Capture; }(); +} + +static_assert(tfn<int>() == 42, ""); + +constexpr int gfn() { + int Capture = 42; + return [=](auto P) constexpr { return Capture + P; }(58); +} + +static_assert(gfn() == 100, ""); + +constexpr bool OtherCaptures() { + int Capture = 42; + constexpr auto Outer = [](auto P) constexpr { return 42 + P; }; + auto Inner = [&](auto O) constexpr { return O(58) + Capture; }; + return Inner(Outer) == 142; +} + +static_assert(OtherCaptures(), ""); +} // namespace PR36054 + #endif // ndef CPP14_AND_EARLIER Index: clang/lib/Sema/SemaLambda.cpp =================================================================== --- clang/lib/Sema/SemaLambda.cpp +++ clang/lib/Sema/SemaLambda.cpp @@ -1604,17 +1604,27 @@ ExplicitParams, ExplicitResultType, CaptureInits, EndLoc, ContainsUnexpandedParameterPack); - // If the lambda expression's call operator is not explicitly marked constexpr - // and we are not in a dependent context, analyze the call operator to infer - // its constexpr-ness, suppressing diagnostics while doing so. - if (getLangOpts().CPlusPlus17 && !CallOperator->isInvalidDecl() && - !CallOperator->isConstexpr() && - !isa<CoroutineBodyStmt>(CallOperator->getBody()) && - !Class->getDeclContext()->isDependentContext()) { - TentativeAnalysisScope DiagnosticScopeGuard(*this); - CallOperator->setConstexpr( - CheckConstexprFunctionDecl(CallOperator) && - CheckConstexprFunctionBody(CallOperator, CallOperator->getBody())); + + if (!CallOperator->isInvalidDecl()) { + // If the call operator is explicitly marked constexpr, verify that that is + // actually the case. This is done here instead of in + // ActOnFinishFunctionBody because the constexpr evaluator needs access to + // the completed lambda class to check this. + if (CallOperator->isConstexpr()) { + if (!CheckConstexprFunctionDecl(CallOperator) || + !CheckConstexprFunctionBody(CallOperator, CallOperator->getBody())) + CallOperator->setInvalidDecl(); + } + // Otherwise, if we're not in a dependent context, analyze the call operator + // to infer its constexpr-ness, suppressing diagnostics while doing so. + else if (getLangOpts().CPlusPlus17 && + !isa<CoroutineBodyStmt>(CallOperator->getBody()) && + !Class->getDeclContext()->isDependentContext()) { + TentativeAnalysisScope DiagnosticScopeGuard(*this); + CallOperator->setConstexpr( + CheckConstexprFunctionDecl(CallOperator) && + CheckConstexprFunctionBody(CallOperator, CallOperator->getBody())); + } } // Emit delayed shadowing warnings now that the full capture list is known. Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -12883,6 +12883,7 @@ } if (!IsInstantiation && FD && FD->isConstexpr() && !FD->isInvalidDecl() && + !isLambdaCallOperator(FD) && (!CheckConstexprFunctionDecl(FD) || !CheckConstexprFunctionBody(FD, Body))) FD->setInvalidDecl();
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits