forked from OSchip/llvm-project
Partially implement P1401R5 (Narrowing contextual conversions to bool)
Support Narrowing conversions to bool in if constexpr condition under C++23 language mode. Only if constexpr is implemented as the behavior of static_assert is already conforming. Still need to work on explicit(bool) to complete support.
This commit is contained in:
parent
10158b52dc
commit
8747234032
|
@ -82,11 +82,11 @@ def err_typecheck_converted_constant_expression_indirect : Error<
|
||||||
"bind reference to a temporary">;
|
"bind reference to a temporary">;
|
||||||
def err_expr_not_cce : Error<
|
def err_expr_not_cce : Error<
|
||||||
"%select{case value|enumerator value|non-type template argument|"
|
"%select{case value|enumerator value|non-type template argument|"
|
||||||
"array size|constexpr if condition|explicit specifier argument}0 "
|
"array size|explicit specifier argument}0 "
|
||||||
"is not a constant expression">;
|
"is not a constant expression">;
|
||||||
def ext_cce_narrowing : ExtWarn<
|
def ext_cce_narrowing : ExtWarn<
|
||||||
"%select{case value|enumerator value|non-type template argument|"
|
"%select{case value|enumerator value|non-type template argument|"
|
||||||
"array size|constexpr if condition|explicit specifier argument}0 "
|
"array size|explicit specifier argument}0 "
|
||||||
"%select{cannot be narrowed from type %2 to %3|"
|
"%select{cannot be narrowed from type %2 to %3|"
|
||||||
"evaluates to %2, which cannot be narrowed to type %3}1">,
|
"evaluates to %2, which cannot be narrowed to type %3}1">,
|
||||||
InGroup<CXX11Narrowing>, DefaultError, SFINAEFailure;
|
InGroup<CXX11Narrowing>, DefaultError, SFINAEFailure;
|
||||||
|
@ -1487,6 +1487,8 @@ def err_messaging_class_with_direct_method : Error<
|
||||||
// C++ declarations
|
// C++ declarations
|
||||||
def err_static_assert_expression_is_not_constant : Error<
|
def err_static_assert_expression_is_not_constant : Error<
|
||||||
"static_assert expression is not an integral constant expression">;
|
"static_assert expression is not an integral constant expression">;
|
||||||
|
def err_constexpr_if_condition_expression_is_not_constant : Error<
|
||||||
|
"constexpr if condition is not a constant expression">;
|
||||||
def err_static_assert_failed : Error<"static_assert failed%select{ %1|}0">;
|
def err_static_assert_failed : Error<"static_assert failed%select{ %1|}0">;
|
||||||
def err_static_assert_requirement_failed : Error<
|
def err_static_assert_requirement_failed : Error<
|
||||||
"static_assert failed due to requirement '%0'%select{ %2|}1">;
|
"static_assert failed due to requirement '%0'%select{ %2|}1">;
|
||||||
|
|
|
@ -3503,7 +3503,6 @@ public:
|
||||||
CCEK_Enumerator, ///< Enumerator value with fixed underlying type.
|
CCEK_Enumerator, ///< Enumerator value with fixed underlying type.
|
||||||
CCEK_TemplateArg, ///< Value of a non-type template parameter.
|
CCEK_TemplateArg, ///< Value of a non-type template parameter.
|
||||||
CCEK_ArrayBound, ///< Array bound in array declarator or new-expression.
|
CCEK_ArrayBound, ///< Array bound in array declarator or new-expression.
|
||||||
CCEK_ConstexprIf, ///< Condition in a constexpr if statement.
|
|
||||||
CCEK_ExplicitBool ///< Condition in an explicit(bool) specifier.
|
CCEK_ExplicitBool ///< Condition in an explicit(bool) specifier.
|
||||||
};
|
};
|
||||||
ExprResult CheckConvertedConstantExpression(Expr *From, QualType T,
|
ExprResult CheckConvertedConstantExpression(Expr *From, QualType T,
|
||||||
|
|
|
@ -3911,7 +3911,7 @@ ExprResult Sema::CheckConditionVariable(VarDecl *ConditionVar,
|
||||||
|
|
||||||
/// CheckCXXBooleanCondition - Returns true if a conversion to bool is invalid.
|
/// CheckCXXBooleanCondition - Returns true if a conversion to bool is invalid.
|
||||||
ExprResult Sema::CheckCXXBooleanCondition(Expr *CondExpr, bool IsConstexpr) {
|
ExprResult Sema::CheckCXXBooleanCondition(Expr *CondExpr, bool IsConstexpr) {
|
||||||
// C++ 6.4p4:
|
// C++11 6.4p4:
|
||||||
// The value of a condition that is an initialized declaration in a statement
|
// The value of a condition that is an initialized declaration in a statement
|
||||||
// other than a switch statement is the value of the declared variable
|
// other than a switch statement is the value of the declared variable
|
||||||
// implicitly converted to type bool. If that conversion is ill-formed, the
|
// implicitly converted to type bool. If that conversion is ill-formed, the
|
||||||
|
@ -3919,12 +3919,22 @@ ExprResult Sema::CheckCXXBooleanCondition(Expr *CondExpr, bool IsConstexpr) {
|
||||||
// The value of a condition that is an expression is the value of the
|
// The value of a condition that is an expression is the value of the
|
||||||
// expression, implicitly converted to bool.
|
// expression, implicitly converted to bool.
|
||||||
//
|
//
|
||||||
|
// C++2b 8.5.2p2
|
||||||
|
// If the if statement is of the form if constexpr, the value of the condition
|
||||||
|
// is contextually converted to bool and the converted expression shall be
|
||||||
|
// a constant expression.
|
||||||
|
//
|
||||||
|
|
||||||
|
ExprResult E = PerformContextuallyConvertToBool(CondExpr);
|
||||||
|
if (!IsConstexpr || E.isInvalid() || E.get()->isValueDependent())
|
||||||
|
return E;
|
||||||
|
|
||||||
// FIXME: Return this value to the caller so they don't need to recompute it.
|
// FIXME: Return this value to the caller so they don't need to recompute it.
|
||||||
llvm::APSInt Value(/*BitWidth*/1);
|
llvm::APSInt Cond;
|
||||||
return (IsConstexpr && !CondExpr->isValueDependent())
|
E = VerifyIntegerConstantExpression(
|
||||||
? CheckConvertedConstantExpression(CondExpr, Context.BoolTy, Value,
|
E.get(), &Cond,
|
||||||
CCEK_ConstexprIf)
|
diag::err_constexpr_if_condition_expression_is_not_constant);
|
||||||
: PerformContextuallyConvertToBool(CondExpr);
|
return E;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to determine whether this is the (deprecated) C++
|
/// Helper function to determine whether this is the (deprecated) C++
|
||||||
|
|
|
@ -5634,12 +5634,8 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From,
|
||||||
// implicitly converted to type T, where the converted
|
// implicitly converted to type T, where the converted
|
||||||
// expression is a constant expression and the implicit conversion
|
// expression is a constant expression and the implicit conversion
|
||||||
// sequence contains only [... list of conversions ...].
|
// sequence contains only [... list of conversions ...].
|
||||||
// C++1z [stmt.if]p2:
|
|
||||||
// If the if statement is of the form if constexpr, the value of the
|
|
||||||
// condition shall be a contextually converted constant expression of type
|
|
||||||
// bool.
|
|
||||||
ImplicitConversionSequence ICS =
|
ImplicitConversionSequence ICS =
|
||||||
CCE == Sema::CCEK_ConstexprIf || CCE == Sema::CCEK_ExplicitBool
|
CCE == Sema::CCEK_ExplicitBool
|
||||||
? TryContextuallyConvertToBool(S, From)
|
? TryContextuallyConvertToBool(S, From)
|
||||||
: TryCopyInitialization(S, From, T,
|
: TryCopyInitialization(S, From, T,
|
||||||
/*SuppressUserConversions=*/false,
|
/*SuppressUserConversions=*/false,
|
||||||
|
|
|
@ -38,18 +38,38 @@ namespace odr_use_in_selected_arm {
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
namespace ccce {
|
namespace ccce {
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
};
|
||||||
void f() {
|
void f() {
|
||||||
if (5) {}
|
if (5) {}
|
||||||
if constexpr (5) {} // expected-error {{cannot be narrowed}}
|
if constexpr (5) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
template<int N> void g() {
|
template<int N> void g() {
|
||||||
if constexpr (N) {} // expected-error {{cannot be narrowed}}
|
if constexpr (N) {
|
||||||
}
|
}
|
||||||
template void g<5>(); // expected-note {{instantiation of}}
|
}
|
||||||
|
template void g<5>();
|
||||||
void h() {
|
void h() {
|
||||||
if constexpr (4.3) {} // expected-error{{conversion from 'double' to 'bool' is not allowed in a converted constant expression}}
|
if constexpr (4.3) { //expected-warning {{implicit conversion from 'double' to 'bool' changes value}}
|
||||||
|
}
|
||||||
constexpr void *p = nullptr;
|
constexpr void *p = nullptr;
|
||||||
if constexpr (p) {} // expected-error{{conversion from 'void *const' to 'bool' is not allowed in a converted constant expression}}
|
if constexpr (p) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void not_constant(int b, S s) { // expected-note 2{{declared here}}
|
||||||
|
if constexpr (bool(b)) { // expected-error {{constexpr if condition is not a constant expression}} expected-note {{cannot be used in a constant expression}}
|
||||||
|
}
|
||||||
|
if constexpr (b) { // expected-error {{constexpr if condition is not a constant expression}} expected-note {{cannot be used in a constant expression}}
|
||||||
|
}
|
||||||
|
if constexpr (s) { // expected-error {{value of type 'ccce::S' is not contextually convertible to 'bool'}}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr S constexprS;
|
||||||
|
if constexpr (constexprS) { // expected-error {{value of type 'const ccce::S' is not contextually convertible to 'bool'}}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -188,3 +188,14 @@ void foo4(T t) {
|
||||||
}
|
}
|
||||||
void callFoo4() { foo4(42); }
|
void callFoo4() { foo4(42); }
|
||||||
// expected-note@-1{{in instantiation of function template specialization 'foo4<int>' requested here}}
|
// expected-note@-1{{in instantiation of function template specialization 'foo4<int>' requested here}}
|
||||||
|
|
||||||
|
static_assert(42, "message");
|
||||||
|
static_assert(42.0, "message"); // expected-warning {{implicit conversion from 'double' to 'bool' changes value from 42 to true}}
|
||||||
|
constexpr int *p = 0;
|
||||||
|
static_assert(p, "message"); // expected-error {{static_assert failed}}
|
||||||
|
|
||||||
|
struct NotBool {
|
||||||
|
} notBool;
|
||||||
|
constexpr NotBool constexprNotBool;
|
||||||
|
static_assert(notBool, "message"); // expected-error {{value of type 'struct NotBool' is not contextually convertible to 'bool'}}
|
||||||
|
static_assert(constexprNotBool, "message"); // expected-error {{value of type 'const NotBool' is not contextually convertible to 'bool'}}
|
||||||
|
|
|
@ -1296,7 +1296,7 @@ C++20, informally referred to as C++2b.</p>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Narrowing contextual conversions to bool</td>
|
<td>Narrowing contextual conversions to bool</td>
|
||||||
<td><a href="https://wg21.link/P1401R5">P1401R5</a></td>
|
<td><a href="https://wg21.link/P1401R5">P1401R5</a></td>
|
||||||
<td class="none" align="center">No</td>
|
<td class="partial" align="center">Clang 13</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Trimming whitespaces before line splicing</td>
|
<td>Trimming whitespaces before line splicing</td>
|
||||||
|
|
Loading…
Reference in New Issue