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()) { + if (const ReferenceType *DestTypeTmp = DestType->getAs()) { // 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,28 @@ } if (isa(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(); + + 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 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 char *PR21845() { return const_cast((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(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(CA{}); + +void f() { + typedef void (*F)(); + F &&f = F{}; + (void)const_cast(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{}); // expected-error {{const_cast from rvalue to reference type 'F &&' (aka 'void (*&&)()')}} + + class C; + typedef void (C::*CF)(); + CF &&cf = CF{}; + (void)const_cast(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{}); // expected-error {{const_cast from rvalue to reference type 'CF &&' (aka 'void (C::*&&)()')}} + + typedef int C::*CM; + CM &&cm = CM{}; + (void)const_cast(CM{}); + (void)const_cast(CM{}); // expected-error {{const_cast from rvalue to reference type 'CM &&' (aka 'int C::*&&')}} +} + +} // namespace N4261 + +#endif