[Diagnostics] Warn for std::is_constant_evaluated in constexpr mode

Summary:
constexpr int fn1() {
  if constexpr (std::is_constant_evaluated()) // condition is always true!
    return 0;
  else
    return 1;
}

constexpr int fn2() {
  if (std::is_constant_evaluated())
    return 0;
  else
    return 1;
}

Solves PR42977

Reviewers: rsmith, aaron.ballman

Reviewed By: rsmith

Subscribers: cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D69518
This commit is contained in:
Dávid Bolvanský 2019-10-29 19:06:48 +01:00
parent c950495405
commit b06305e449
4 changed files with 78 additions and 2 deletions

View File

@ -329,6 +329,10 @@ def warn_integer_constant_overflow : Warning<
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 a manifestly constant-evaluated expression">,
InGroup<DiagGroup<"constant-evaluated">>;
// inline asm related.
let CategoryName = "Inline Assembly Issue" in {
def err_asm_invalid_escape : Error<

View File

@ -10593,8 +10593,24 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
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 && Callee->isInStdNamespace() &&
Callee->getIdentifier() &&
Callee->getIdentifier()->isStr("is_constant_evaluated")))) {
// FIXME: Find a better way to avoid duplicated diagnostics.
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:

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -std=c++2a -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu
// RUN: %clang_cc1 -std=c++2a -verify %s -fcxx-exceptions -Wno-constant-evaluated -triple=x86_64-linux-gnu
using size_t = decltype(sizeof(int));

View File

@ -0,0 +1,56 @@
// RUN: %clang_cc1 -std=c++2a -fsyntax-only -verify %s
namespace std {
constexpr bool is_constant_evaluated() noexcept {
return __builtin_is_constant_evaluated();
}
} // namespace std
constexpr int fn1() {
if constexpr (std::is_constant_evaluated()) // expected-warning {{'std::is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}}
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 a manifestly constant-evaluated expression}}
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 a manifestly constant-evaluated expression}}
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 a manifestly constant-evaluated expression}}
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 a manifestly constant-evaluated expression}}
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;
}