From 8747234032c9c6270a6198ab3cca14ce2bd18721 Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Mon, 12 Jul 2021 08:03:51 -0400 Subject: [PATCH] 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. --- .../clang/Basic/DiagnosticSemaKinds.td | 6 ++-- clang/include/clang/Sema/Sema.h | 1 - clang/lib/Sema/SemaExprCXX.cpp | 22 ++++++++++---- clang/lib/Sema/SemaOverload.cpp | 6 +--- .../CXX/stmt.stmt/stmt.select/stmt.if/p2.cpp | 30 +++++++++++++++---- clang/test/SemaCXX/static-assert.cpp | 11 +++++++ clang/www/cxx_status.html | 2 +- 7 files changed, 58 insertions(+), 20 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index c8d848b41d35..21b9ce2d9ff2 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -82,11 +82,11 @@ def err_typecheck_converted_constant_expression_indirect : Error< "bind reference to a temporary">; def err_expr_not_cce : Error< "%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">; def ext_cce_narrowing : ExtWarn< "%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|" "evaluates to %2, which cannot be narrowed to type %3}1">, InGroup, DefaultError, SFINAEFailure; @@ -1487,6 +1487,8 @@ def err_messaging_class_with_direct_method : Error< // C++ declarations def err_static_assert_expression_is_not_constant : Error< "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_requirement_failed : Error< "static_assert failed due to requirement '%0'%select{ %2|}1">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index ad987dffac03..e1dac7db1b4c 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3503,7 +3503,6 @@ public: CCEK_Enumerator, ///< Enumerator value with fixed underlying type. CCEK_TemplateArg, ///< Value of a non-type template parameter. 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. }; ExprResult CheckConvertedConstantExpression(Expr *From, QualType T, diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index e6bf5c6a7ad8..bf0b8b71d931 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -3911,7 +3911,7 @@ ExprResult Sema::CheckConditionVariable(VarDecl *ConditionVar, /// CheckCXXBooleanCondition - Returns true if a conversion to bool is invalid. 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 // other than a switch statement is the value of the declared variable // 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 // 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. - llvm::APSInt Value(/*BitWidth*/1); - return (IsConstexpr && !CondExpr->isValueDependent()) - ? CheckConvertedConstantExpression(CondExpr, Context.BoolTy, Value, - CCEK_ConstexprIf) - : PerformContextuallyConvertToBool(CondExpr); + llvm::APSInt Cond; + E = VerifyIntegerConstantExpression( + E.get(), &Cond, + diag::err_constexpr_if_condition_expression_is_not_constant); + return E; } /// Helper function to determine whether this is the (deprecated) C++ diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 8092f0be6cce..0758fbb84107 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -5634,12 +5634,8 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From, // implicitly converted to type T, where the converted // expression is a constant expression and the implicit conversion // 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 = - CCE == Sema::CCEK_ConstexprIf || CCE == Sema::CCEK_ExplicitBool + CCE == Sema::CCEK_ExplicitBool ? TryContextuallyConvertToBool(S, From) : TryCopyInitialization(S, From, T, /*SuppressUserConversions=*/false, diff --git a/clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p2.cpp b/clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p2.cpp index c33bf0eba231..ed61119dc252 100644 --- a/clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p2.cpp +++ b/clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p2.cpp @@ -38,18 +38,38 @@ namespace odr_use_in_selected_arm { } #else namespace ccce { + + struct S { + }; void f() { if (5) {} - if constexpr (5) {} // expected-error {{cannot be narrowed}} + if constexpr (5) { + } } template 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() { - 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; - 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'}} + } } } diff --git a/clang/test/SemaCXX/static-assert.cpp b/clang/test/SemaCXX/static-assert.cpp index dfc06331e51f..affc0c32a7a8 100644 --- a/clang/test/SemaCXX/static-assert.cpp +++ b/clang/test/SemaCXX/static-assert.cpp @@ -188,3 +188,14 @@ void foo4(T t) { } void callFoo4() { foo4(42); } // expected-note@-1{{in instantiation of function template specialization 'foo4' 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'}} diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 8de688189e29..2ad2047f68d0 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -1296,7 +1296,7 @@ C++20, informally referred to as C++2b.

Narrowing contextual conversions to bool P1401R5 - No + Clang 13 Trimming whitespaces before line splicing