diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -2777,6 +2777,13 @@ FD->getIdentifier() && FD->getIdentifier()->isStr("move"); } + bool isCallToStdIsConstantEvaluated() const { + const FunctionDecl *FD = getDirectCallee(); + return getNumArgs() == 0 && FD && FD->isInStdNamespace() && + FD->getIdentifier() && + FD->getIdentifier()->isStr("is_constant_evaluated"); + } + static bool classof(const Stmt *T) { return T->getStmtClass() >= firstCallExprConstant && T->getStmtClass() <= lastCallExprConstant; diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -329,6 +329,10 @@ def note_unimplemented_constexpr_lambda_feature_ast : Note< "unimplemented constexpr lambda feature: %0 (coming soon!)">; +def warn_is_constant_evaluated_always_true_constexpr : Warning< + "'%0' will always evaluate to 'true' in constexpr context">, + InGroup>; + // inline asm related. let CategoryName = "Inline Assembly Issue" in { def err_asm_invalid_escape : Error< diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -10590,8 +10590,23 @@ return false; } - case Builtin::BI__builtin_is_constant_evaluated: + case Builtin::BI__builtin_is_constant_evaluated: { + const auto *Callee = Info.CurrentCall->getCallee(); + if (Info.InConstantContext && !Info.CheckingPotentialConstantExpression && + (Info.CallStackDepth == 1 || + (Info.CallStackDepth == 2 && E->getNumArgs() == 0 && + Callee->isInStdNamespace() && Callee->getIdentifier() && + Callee->getIdentifier()->isStr("is_constant_evaluated")))) { + if (Info.EvalStatus.Diag) + Info.report((Info.CallStackDepth == 1) ? E->getExprLoc() + : Info.CurrentCall->CallLoc, + diag::warn_is_constant_evaluated_always_true_constexpr) + << (Info.CallStackDepth == 1 ? "__builtin_is_constant_evaluated" + : "std::is_constant_evaluated"); + } + return Success(Info.InConstantContext, E); + } case Builtin::BI__builtin_ctz: case Builtin::BI__builtin_ctzl: diff --git a/clang/test/SemaCXX/warn-std-is-constant-evaluated-constexpr.cpp b/clang/test/SemaCXX/warn-std-is-constant-evaluated-constexpr.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/warn-std-is-constant-evaluated-constexpr.cpp @@ -0,0 +1,57 @@ +// RUN: %clang_cc1 -std=c++2a -fsyntax-only -verify %s + +namespace std { +constexpr bool is_constant_evaluated() noexcept { + return __builtin_is_constant_evaluated(); +} +} + +constexpr int fn1() { + if constexpr (std::is_constant_evaluated()) // expected-warning {{'std::is_constant_evaluated' will always evaluate to 'true' in constexpr context}} + return 0; + else + return 1; +} + +constexpr int fn2() { + if constexpr (!std::is_constant_evaluated()) // expected-warning {{'std::is_constant_evaluated' will always evaluate to 'true' in constexpr context}} + return 0; + else + return 1; +} + +constexpr int fn3() { + if constexpr (std::is_constant_evaluated() == false) // expected-warning {{'std::is_constant_evaluated' will always evaluate to 'true' in constexpr context}} + return 0; + else + return 1; +} + +constexpr int fn4() { + if constexpr (__builtin_is_constant_evaluated() == true) // expected-warning {{'__builtin_is_constant_evaluated' will always evaluate to 'true' in constexpr context}} + return 0; + else + return 1; +} + + +constexpr int fn5() { + if constexpr (__builtin_is_constant_evaluated()) // expected-warning {{'__builtin_is_constant_evaluated' will always evaluate to 'true' in constexpr context}} + return 0; + else + return 1; +} + +constexpr int nowarn1() { + if (std::is_constant_evaluated()) + return 0; + else + return 1; +} + +constexpr int nowarn2() { + if (!__builtin_is_constant_evaluated()) + return 0; + else + return 1; +}