[clang] 9ce6dc9 - CWG1423: don't permit implicit conversion of nullptr_t to bool.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 11 06:53:04 PST 2020


Author: Richard Smith
Date: 2020-02-11T06:52:45-08:00
New Revision: 9ce6dc9872be4081fb98f6161c28581e1cbbe7dc

URL: https://github.com/llvm/llvm-project/commit/9ce6dc9872be4081fb98f6161c28581e1cbbe7dc
DIFF: https://github.com/llvm/llvm-project/commit/9ce6dc9872be4081fb98f6161c28581e1cbbe7dc.diff

LOG: CWG1423: don't permit implicit conversion of nullptr_t to bool.

The C++ rules briefly allowed this, but the rule changed nearly 10 years
ago and we never updated our implementation to match. However, we've
warned on this by default for a long time, and no other compiler accepts
(even as an extension).

Added: 
    

Modified: 
    clang/include/clang/Sema/Overload.h
    clang/lib/Sema/SemaInit.cpp
    clang/lib/Sema/SemaOverload.cpp
    clang/test/CXX/drs/dr14xx.cpp
    clang/test/CXX/drs/dr6xx.cpp
    clang/test/CXX/expr/expr.const/p3-0x.cpp
    clang/test/CodeGenCXX/nullptr.cpp
    clang/test/SemaCXX/conversion.cpp
    clang/test/SemaCXX/nullptr.cpp
    clang/www/cxx_dr_status.html

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index a274102ceb11..f1a8b98e5efd 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -677,6 +677,24 @@ class Sema;
       StdInitializerListElement = V;
     }
 
+    /// Form an "implicit" conversion sequence from nullptr_t to bool, for a
+    /// direct-initialization of a bool object from nullptr_t.
+    static ImplicitConversionSequence getNullptrToBool(QualType SourceType,
+                                                       QualType DestType,
+                                                       bool NeedLValToRVal) {
+      ImplicitConversionSequence ICS;
+      ICS.setStandard();
+      ICS.Standard.setAsIdentityConversion();
+      ICS.Standard.setFromType(SourceType);
+      if (NeedLValToRVal)
+        ICS.Standard.First = ICK_Lvalue_To_Rvalue;
+      ICS.Standard.setToType(0, SourceType);
+      ICS.Standard.Second = ICK_Boolean_Conversion;
+      ICS.Standard.setToType(1, DestType);
+      ICS.Standard.setToType(2, DestType);
+      return ICS;
+    }
+
     // The result of a comparison between implicit conversion
     // sequences. Use Sema::CompareImplicitConversionSequences to
     // actually perform the comparison.

diff  --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 61b7c166239f..61bb1f053ce2 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -4420,16 +4420,20 @@ static void TryListInitialization(Sema &S,
     // direct-list-initialization and copy-initialization otherwise.
     // We can't use InitListChecker for this, because it always performs
     // copy-initialization. This only matters if we might use an 'explicit'
-    // conversion operator, so we only need to handle the cases where the source
-    // is of record type.
-    if (InitList->getInit(0)->getType()->isRecordType()) {
+    // conversion operator, or for the special case conversion of nullptr_t to
+    // bool, so we only need to handle those cases.
+    //
+    // FIXME: Why not do this in all cases?
+    Expr *Init = InitList->getInit(0);
+    if (Init->getType()->isRecordType() ||
+        (Init->getType()->isNullPtrType() && DestType->isBooleanType())) {
       InitializationKind SubKind =
           Kind.getKind() == InitializationKind::IK_DirectList
               ? InitializationKind::CreateDirect(Kind.getLocation(),
                                                  InitList->getLBraceLoc(),
                                                  InitList->getRBraceLoc())
               : Kind;
-      Expr *SubInit[1] = { InitList->getInit(0) };
+      Expr *SubInit[1] = { Init };
       Sequence.InitializeFrom(S, Entity, SubKind, SubInit,
                               /*TopLevelOfInitList*/true,
                               TreatUnavailableAsInvalid);
@@ -5854,6 +5858,19 @@ void InitializationSequence::InitializeFrom(Sema &S,
     return;
   }
 
+  //    - Otherwise, if the initialization is direct-initialization, the source
+  //    type is std::nullptr_t, and the destination type is bool, the initial
+  //    value of the object being initialized is false.
+  if (!SourceType.isNull() && SourceType->isNullPtrType() &&
+      DestType->isBooleanType() &&
+      Kind.getKind() == InitializationKind::IK_Direct) {
+    AddConversionSequenceStep(
+        ImplicitConversionSequence::getNullptrToBool(SourceType, DestType,
+                                                     Initializer->isGLValue()),
+        DestType);
+    return;
+  }
+
   //    - Otherwise, the initial value of the object being initialized is the
   //      (possibly converted) value of the initializer expression. Standard
   //      conversions (Clause 4) will be used, if necessary, to convert the

diff  --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 1e838fd75130..a10b7cd76482 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -230,7 +230,6 @@ bool StandardConversionSequence::isPointerConversionToBool() const {
        getFromType()->isMemberPointerType() ||
        getFromType()->isObjCObjectPointerType() ||
        getFromType()->isBlockPointerType() ||
-       getFromType()->isNullPtrType() ||
        First == ICK_Array_To_Pointer || First == ICK_Function_To_Pointer))
     return true;
 
@@ -1847,8 +1846,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
              (FromType->isArithmeticType() ||
               FromType->isAnyPointerType() ||
               FromType->isBlockPointerType() ||
-              FromType->isMemberPointerType() ||
-              FromType->isNullPtrType())) {
+              FromType->isMemberPointerType())) {
     // Boolean conversions (C++ 4.12).
     SCS.Second = ICK_Boolean_Conversion;
     FromType = S.Context.BoolTy;
@@ -5437,6 +5435,17 @@ Sema::PerformObjectArgumentInitialization(Expr *From,
 /// expression From to bool (C++0x [conv]p3).
 static ImplicitConversionSequence
 TryContextuallyConvertToBool(Sema &S, Expr *From) {
+  // C++ [dcl.init]/17.8:
+  //   - Otherwise, if the initialization is direct-initialization, the source
+  //     type is std::nullptr_t, and the destination type is bool, the initial
+  //     value of the object being initialized is false.
+  if (From->getType()->isNullPtrType())
+    return ImplicitConversionSequence::getNullptrToBool(From->getType(),
+                                                        S.Context.BoolTy,
+                                                        From->isGLValue());
+
+  // All other direct-initialization of bool is equivalent to an implicit
+  // conversion to bool in which explicit conversions are permitted.
   return TryImplicitConversion(S, From, S.Context.BoolTy,
                                /*SuppressUserConversions=*/false,
                                AllowedExplicit::Conversions,

diff  --git a/clang/test/CXX/drs/dr14xx.cpp b/clang/test/CXX/drs/dr14xx.cpp
index 52129844c418..d55427f5de8c 100644
--- a/clang/test/CXX/drs/dr14xx.cpp
+++ b/clang/test/CXX/drs/dr14xx.cpp
@@ -8,6 +8,15 @@
 // expected-no-diagnostics
 #endif
 
+namespace dr1423 { // dr1423: 11
+#if __cplusplus >= 201103L
+  bool b1 = nullptr; // expected-error {{cannot initialize}}
+  bool b2(nullptr); // expected-warning {{implicit conversion of nullptr constant to 'bool'}}
+  bool b3 = {nullptr}; // expected-error {{cannot initialize}}
+  bool b4{nullptr}; // expected-warning {{implicit conversion of nullptr constant to 'bool'}}
+#endif
+}
+
 // dr1425: na abi
 
 namespace dr1460 { // dr1460: 3.5

diff  --git a/clang/test/CXX/drs/dr6xx.cpp b/clang/test/CXX/drs/dr6xx.cpp
index 7a0adb5fe406..4c4ed7767ecd 100644
--- a/clang/test/CXX/drs/dr6xx.cpp
+++ b/clang/test/CXX/drs/dr6xx.cpp
@@ -585,10 +585,10 @@ namespace dr652 { // dr652: yes
 // dr653 FIXME: add codegen test
 
 #if __cplusplus >= 201103L
-namespace dr654 { // dr654: yes
+namespace dr654 { // dr654: sup 1423
   void f() {
     if (nullptr) {} // expected-warning {{implicit conversion of nullptr constant to 'bool'}}
-    bool b = nullptr; // expected-warning {{implicit conversion of nullptr constant to 'bool'}}
+    bool b = nullptr; // expected-error {{cannot initialize a variable of type 'bool' with an rvalue of type 'nullptr_t'}}
     if (nullptr == 0) {}
     if (nullptr != 0) {}
     if (nullptr <= 0) {} // expected-error {{invalid operands}}

diff  --git a/clang/test/CXX/expr/expr.const/p3-0x.cpp b/clang/test/CXX/expr/expr.const/p3-0x.cpp
index 731e0c312fa1..8daca7a565f3 100644
--- a/clang/test/CXX/expr/expr.const/p3-0x.cpp
+++ b/clang/test/CXX/expr/expr.const/p3-0x.cpp
@@ -95,7 +95,7 @@ Val<decltype(&noexcept_true), &noexcept_false> add_noexcept;
 using Int = A<1.0>; // expected-error {{conversion from 'double' to 'unsigned char' is not allowed in a converted constant expression}}
 enum B : bool {
   True = &a, // expected-error {{conversion from 'bool (*)(int)' to 'bool' is not allowed in a converted constant expression}}
-  False = nullptr // expected-error {{conversion from 'nullptr_t' to 'bool' is not allowed in a converted constant expression}}
+  False = 0.0, // expected-error {{conversion from 'double' to 'bool' is not allowed in a converted constant expression}}
 };
 void c() {
   // Note, promoted type of switch is 'int'.

diff  --git a/clang/test/CodeGenCXX/nullptr.cpp b/clang/test/CodeGenCXX/nullptr.cpp
index 823c0d7d18a7..ab47282569fd 100644
--- a/clang/test/CodeGenCXX/nullptr.cpp
+++ b/clang/test/CodeGenCXX/nullptr.cpp
@@ -32,7 +32,7 @@ union U {
 // CHECK: load
 // CHECK-NOT: load
 // CHECK: ret i1 false
-bool pr23833_a(U &u) { return u.b; }
+bool pr23833_a(U &u) { return bool(u.b); }
 
 // CHECK-LABEL: define {{.*}}pr23833_b
 // CHECK: store

diff  --git a/clang/test/SemaCXX/conversion.cpp b/clang/test/SemaCXX/conversion.cpp
index dcd64fa2ec8a..67bfdf5532b5 100644
--- a/clang/test/SemaCXX/conversion.cpp
+++ b/clang/test/SemaCXX/conversion.cpp
@@ -1,5 +1,5 @@
 // RUN: %clang_cc1 -triple x86_64-apple-darwin -fsyntax-only -Wconversion -std=c++11 -verify %s
-// RUN: %clang_cc1 -triple x86_64-apple-darwin -fsyntax-only -Wconversion -std=c++11 %s 2>&1 | FileCheck %s
+// RUN: not %clang_cc1 -triple x86_64-apple-darwin -fsyntax-only -Wconversion -std=c++11 %s 2>&1 | FileCheck %s
 
 #include <stddef.h>
 
@@ -129,9 +129,9 @@ namespace test6 {
 
 namespace test7 {
   bool fun() {
-    bool x = nullptr; // expected-warning {{implicit conversion of nullptr constant to 'bool'}}
+    bool x = nullptr; // expected-error {{cannot initialize}}
     if (nullptr) {} // expected-warning {{implicit conversion of nullptr constant to 'bool'}}
-    return nullptr; // expected-warning {{implicit conversion of nullptr constant to 'bool'}}
+    return nullptr; // expected-error {{cannot initialize}}
   }
 }
 
@@ -198,14 +198,12 @@ namespace test8 {
   }
 }
 
-// Don't warn on a nullptr to bool conversion when the nullptr is the return
-// type of a function.
 namespace test9 {
   typedef decltype(nullptr) nullptr_t;
   nullptr_t EXIT();
 
   bool test() {
-    return EXIT();
+    return EXIT(); // expected-error {{cannot initialize}}
   }
 }
 
@@ -273,10 +271,10 @@ void function1(const char* str) {
   CHECK13(check_str_null_13(str));
 }
 
-bool some_bool_function(bool);
+bool some_bool_function(bool); // expected-note {{no known conversion}}
 void function2() {
-  CHECK13(some_bool_function(nullptr));  // expected-warning{{implicit conversion of nullptr constant to 'bool'}}
-  CHECK13(some_bool_function(NULL));  // expected-warning{{implicit conversion of NULL constant to 'bool'}}
+  CHECK13(some_bool_function(nullptr));  // expected-error {{no matching function}}
+  CHECK13(some_bool_function(NULL));  // expected-warning {{implicit conversion of NULL constant to 'bool'}}
 }
 
 #define run_check_nullptr_13(str) \

diff  --git a/clang/test/SemaCXX/nullptr.cpp b/clang/test/SemaCXX/nullptr.cpp
index 9a092910b6f9..23ea383c3e87 100644
--- a/clang/test/SemaCXX/nullptr.cpp
+++ b/clang/test/SemaCXX/nullptr.cpp
@@ -25,7 +25,7 @@ nullptr_t f(nullptr_t null)
   pf = null;
   void (A::*pmf)() = nullptr;
   pmf = null;
-  bool b = nullptr;
+  bool b = nullptr; // expected-error {{cannot initialize}}
 
   // Can't convert nullptr to integral implicitly.
   uintptr_t i = nullptr; // expected-error {{cannot initialize}}

diff  --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index d4c6175a8c1b..3e9210f861ab 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -1504,7 +1504,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://wg21.link/cwg244">244</a></td>
     <td>CD1</td>
     <td>Destructor lookup</td>
-    <td class="partial" align="center">Partial</td>
+    <td class="unreleased" align="center">Clang 11</td>
   </tr>
   <tr id="245">
     <td><a href="https://wg21.link/cwg245">245</a></td>
@@ -3967,7 +3967,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://wg21.link/cwg654">654</a></td>
     <td>CD1</td>
     <td>Conversions to and from <TT>nullptr_t</TT></td>
-    <td class="full" align="center">Yes</td>
+    <td class="unreleased" align="center">Superseded by <a href="#1423">1423</a></td>
   </tr>
   <tr id="655">
     <td><a href="https://wg21.link/cwg655">655</a></td>
@@ -8353,7 +8353,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://wg21.link/cwg1423">1423</a></td>
     <td>CD3</td>
     <td>Convertibility of <TT>nullptr</TT> to <TT>bool</TT></td>
-    <td class="none" align="center">Unknown</td>
+    <td class="unreleased" align="center">Clang 11</td>
   </tr>
   <tr id="1424">
     <td><a href="https://wg21.link/cwg1424">1424</a></td>


        


More information about the cfe-commits mailing list