[PATCH] D87565: [Sema] Improve const_cast conformance to N4261

Mark de Wever via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Sun Oct 11 05:16:27 PDT 2020


Mordante updated this revision to Diff 297456.
Mordante added a comment.

Match the behavior of `const_cast` to match GCC's behavior. N4261 was proposed as solution to DR330. Since GCC allows the new `const_cast` behavior retroactively the patch also matches that behavior and no longer restricts the new usage to C++17.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87565/new/

https://reviews.llvm.org/D87565

Files:
  clang/lib/Sema/SemaCast.cpp
  clang/test/SemaCXX/const-cast.cpp


Index: clang/test/SemaCXX/const-cast.cpp
===================================================================
--- clang/test/SemaCXX/const-cast.cpp
+++ clang/test/SemaCXX/const-cast.cpp
@@ -1,6 +1,9 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
 
 struct A {};
 
@@ -80,3 +83,35 @@
 
 template <typename T>
 char *PR21845() { return const_cast<char *>((void)T::x); } // expected-error {{const_cast from 'void' to 'char *' is not allowed}}
+
+#if __cplusplus >= 201103L
+namespace N4261 {
+typedef int *A[3];
+typedef const int *const CA[3];
+
+CA &&r = A{};
+A &&r1 = const_cast<A>(CA{}); // expected-error {{const_cast to 'N4261::A' (aka 'int *[3]'), which is not a reference, pointer-to-object, or pointer-to-data-member}}
+
+A &&r2 = const_cast<A &&>(CA{});
+
+void f() {
+  typedef void (*F)();
+  F &&f = F{};
+  (void)const_cast<F>(F{}); // expected-error {{const_cast to 'F' (aka 'void (*)()'), which is not a reference, pointer-to-object, or pointer-to-data-member}}
+  (void)const_cast<F &&>(F{});
+
+  class C;
+  typedef void (C::*CF)();
+  CF &&cf = CF{};
+  (void)const_cast<CF>(CF{}); // expected-error {{const_cast to 'CF' (aka 'void (C::*)()'), which is not a reference, pointer-to-object, or pointer-to-data-member}}
+  (void)const_cast<CF &&>(CF{});
+
+  typedef int C::*CM;
+  CM &&cm = CM{};
+  (void)const_cast<CM>(CM{});
+  (void)const_cast<CM &&>(CM{});
+}
+
+} // namespace N4261
+
+#endif
Index: clang/lib/Sema/SemaCast.cpp
===================================================================
--- clang/lib/Sema/SemaCast.cpp
+++ clang/lib/Sema/SemaCast.cpp
@@ -1780,7 +1780,7 @@
   QualType SrcType = SrcExpr.get()->getType();
   bool NeedToMaterializeTemporary = false;
 
-  if (const ReferenceType *DestTypeTmp =DestType->getAs<ReferenceType>()) {
+  if (const ReferenceType *DestTypeTmp = DestType->getAs<ReferenceType>()) {
     // C++11 5.2.11p4:
     //   if a pointer to T1 can be explicitly converted to the type "pointer to
     //   T2" using a const_cast, then the following conversions can also be
@@ -1801,16 +1801,29 @@
     }
 
     if (isa<RValueReferenceType>(DestTypeTmp) && SrcExpr.get()->isRValue()) {
-      if (!SrcType->isRecordType()) {
+      // C++17 [expr.const.cast]p3
+      // For two similar types T1 and T2, a prvalue of type T1 may be explicitly
+      // converted to the type T2 using a const_cast if, considering the
+      // cv-decompositions of both types, each P1i is the same as P2i for all i.
+      // The result of a const_cast refers to the original entity.
+      //
+      // The test for the similarity is done later in this function. Here it
+      // only avoids issuing an diagnostic for possible similar types.
+      //
+      // [expr.const.cast]p4
+      // The result of a reference const_cast refers to the original object if
+      // the operand is a glvalue and to the result of applying the temporary
+      // materialization conversion otherwise.
+      NeedToMaterializeTemporary =
+          SrcType->isRecordType() || SrcType->isArrayType() ||
+          SrcType->isFunctionPointerType() || SrcType->isMemberPointerType();
+
+      if (!NeedToMaterializeTemporary) {
         // Cannot const_cast non-class prvalue to rvalue reference type. But if
         // this is C-style, static_cast can do this.
         msg = diag::err_bad_cxx_cast_rvalue;
         return TC_NotApplicable;
       }
-
-      // Materialize the class prvalue so that the const_cast can bind a
-      // reference to it.
-      NeedToMaterializeTemporary = true;
     }
 
     // It's not completely clear under the standard whether we can


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D87565.297456.patch
Type: text/x-patch
Size: 3884 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20201011/2db02b9b/attachment.bin>


More information about the cfe-commits mailing list