Index: clang/lib/Sema/SemaCast.cpp =================================================================== --- clang/lib/Sema/SemaCast.cpp +++ clang/lib/Sema/SemaCast.cpp @@ -1769,7 +1769,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 @@ -1790,16 +1790,23 @@ } if (isa(DestTypeTmp) && SrcExpr.get()->isRValue()) { - if (!SrcType->isRecordType()) { + // Materialize the class prvalue so that the const_cast can bind a + // reference to it. + NeedToMaterializeTemporary = 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. + // + // Prior to C++17 only RecordTypes were allowed. + if (!NeedToMaterializeTemporary && !Self.getLangOpts().CPlusPlus17) { // 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 {}; @@ -71,12 +74,30 @@ f fp2 = const_cast(fp1); // expected-error {{const_cast to 'f' (aka 'int (*)(int)'), which is not a reference, pointer-to-object, or pointer-to-data-member}} void (A::*mfn)() = 0; (void)const_cast(mfn); // expected-error-re {{const_cast to 'void (A::*)(){{( __attribute__\(\(thiscall\)\))?}}', which is not a reference, pointer-to-object, or pointer-to-data-member}} - (void)const_cast(0); // expected-error {{const_cast from rvalue to reference type 'int &&'}} + (void)const_cast(0); +#if __cplusplus < 201703L // N4261 + // expected-error@-2 {{const_cast from rvalue to reference type 'int &&'}} +#endif #if __cplusplus <= 199711L // C++03 or earlier modes - // expected-warning@-2 {{rvalue references are a C++11 extension}} + // expected-warning@-5 {{rvalue references are a C++11 extension}} #endif return **var3; } 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{}); +#if __cplusplus < 201703L +// expected-error@-2 {{const_cast from rvalue to reference type 'N4261::A &&' (aka 'int *(&&)[3]')}} +#endif +} // namespace N4261 +#endif