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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits