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/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8405,6 +8405,9 @@ "%select{self-|array }0comparison always evaluates to " "%select{a constant|true|false|'std::strong_ordering::equal'}1">, InGroup; +def warn_std_is_constant_evaluated_always_true_constexpr : Warning< + "'std::is_constant_evaluated' will always evaluate to " + "'true' in constexpr mode">, InGroup; def warn_comparison_bitwise_always : Warning< "bitwise comparison always evaluates to %select{false|true}0">, InGroup, DefaultIgnore; diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -3664,6 +3664,22 @@ llvm_unreachable("unexpected condition kind"); } +static void checkStdIsConstantEvaluated(Sema &S, Expr *BoolExpr, + bool IsConstexpr) { + if (!IsConstexpr) + return; + BoolExpr = BoolExpr->IgnoreParens(); + CallExpr *Call = nullptr; + UnaryOperator *UO = dyn_cast(BoolExpr); + if (UO && UO->getOpcode() == UO_LNot) + BoolExpr = UO->getSubExpr(); + + Call = dyn_cast(BoolExpr); + if (Call && Call->isCallToStdIsConstantEvaluated()) + S.Diag(Call->getBeginLoc(), + diag::warn_std_is_constant_evaluated_always_true_constexpr); +} + /// CheckCXXBooleanCondition - Returns true if a conversion to bool is invalid. ExprResult Sema::CheckCXXBooleanCondition(Expr *CondExpr, bool IsConstexpr) { // C++ 6.4p4: @@ -3675,6 +3691,7 @@ // expression, implicitly converted to bool. // // FIXME: Return this value to the caller so they don't need to recompute it. + checkStdIsConstantEvaluated(*this, CondExpr, IsConstexpr); llvm::APSInt Value(/*BitWidth*/1); return (IsConstexpr && !CondExpr->isValueDependent()) ? CheckConvertedConstantExpression(CondExpr, Context.BoolTy, Value, 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,36 @@ +// RUN: %clang_cc1 -std=c++2a -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++2a -fsyntax-only -verify -Wtautological-compare %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 mode}} + 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 mode}} + return 0; + else + return 1; +} + +constexpr int nowarn1() { + if (std::is_constant_evaluated()) + return 0; + else + return 1; +} + +constexpr int nowarn2() { + if (std::is_constant_evaluated()) + return 0; + else + return 1; +}