diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index b4e711331970..3b01c22867ac 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -200,8 +200,9 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr, unsigned &msg, CastKind &Kind, CXXCastPath &BasePath, bool ListInitialization); -static TryCastResult TryConstCast(Sema &Self, Expr *SrcExpr, QualType DestType, - bool CStyle, unsigned &msg); +static TryCastResult TryConstCast(Sema &Self, ExprResult &SrcExpr, + QualType DestType, bool CStyle, + unsigned &msg); static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, bool CStyle, const SourceRange &OpRange, @@ -677,7 +678,7 @@ void CastOperation::CheckConstCast() { return; unsigned msg = diag::err_bad_cxx_cast_generic; - if (TryConstCast(Self, SrcExpr.get(), DestType, /*CStyle*/false, msg) != TC_Success + if (TryConstCast(Self, SrcExpr, DestType, /*CStyle*/false, msg) != TC_Success && msg != 0) Self.Diag(OpRange.getBegin(), msg) << CT_Const << SrcExpr.get()->getType() << DestType << OpRange; @@ -1447,12 +1448,26 @@ TryStaticImplicitCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, /// TryConstCast - See if a const_cast from source to destination is allowed, /// and perform it if it is. -static TryCastResult TryConstCast(Sema &Self, Expr *SrcExpr, QualType DestType, - bool CStyle, unsigned &msg) { +static TryCastResult TryConstCast(Sema &Self, ExprResult &SrcExpr, + QualType DestType, bool CStyle, + unsigned &msg) { DestType = Self.Context.getCanonicalType(DestType); - QualType SrcType = SrcExpr->getType(); + QualType SrcType = SrcExpr.get()->getType(); + bool NeedToMaterializeTemporary = false; + if (const ReferenceType *DestTypeTmp =DestType->getAs()) { - if (isa(DestTypeTmp) && !SrcExpr->isLValue()) { + // 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 + // made: + // -- an lvalue of type T1 can be explicitly converted to an lvalue of + // type T2 using the cast const_cast; + // -- a glvalue of type T1 can be explicitly converted to an xvalue of + // type T2 using the cast const_cast; and + // -- if T1 is a class type, a prvalue of type T1 can be explicitly + // converted to an xvalue of type T2 using the cast const_cast. + + if (isa(DestTypeTmp) && !SrcExpr.get()->isLValue()) { // Cannot const_cast non-lvalue to lvalue reference type. But if this // is C-style, static_cast might find a way, so we simply suggest a // message and tell the parent to keep searching. @@ -1460,18 +1475,29 @@ static TryCastResult TryConstCast(Sema &Self, Expr *SrcExpr, QualType DestType, return TC_NotApplicable; } + if (isa(DestTypeTmp) && SrcExpr.get()->isRValue()) { + if (!SrcType->isRecordType()) { + // 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 // const_cast bit-field gl-values. Doing so would not be // intrinsically complicated, but for now, we say no for // consistency with other compilers and await the word of the // committee. - if (SrcExpr->refersToBitField()) { + if (SrcExpr.get()->refersToBitField()) { msg = diag::err_bad_cxx_cast_bitfield; return TC_NotApplicable; } - // C++ 5.2.11p4: An lvalue of type T1 can be [cast] to an lvalue of type T2 - // [...] if a pointer to T1 can be [cast] to the type pointer to T2. DestType = Self.Context.getPointerType(DestTypeTmp->getPointeeType()); SrcType = Self.Context.getPointerType(SrcType); } @@ -1525,6 +1551,13 @@ static TryCastResult TryConstCast(Sema &Self, Expr *SrcExpr, QualType DestType, if (SrcType != DestType) return TC_NotApplicable; + if (NeedToMaterializeTemporary) + // This is a const_cast from a class prvalue to an rvalue reference type. + // Materialize a temporary to store the result of the conversion. + SrcExpr = new (Self.Context) MaterializeTemporaryExpr( + SrcType, SrcExpr.take(), /*IsLValueReference*/ false, + /*ExtendingDecl*/ 0); + return TC_Success; } @@ -1992,8 +2025,10 @@ void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle, // even if a cast resulting from that interpretation is ill-formed. // In plain language, this means trying a const_cast ... unsigned msg = diag::err_bad_cxx_cast_generic; - TryCastResult tcr = TryConstCast(Self, SrcExpr.get(), DestType, + TryCastResult tcr = TryConstCast(Self, SrcExpr, DestType, /*CStyle*/true, msg); + if (SrcExpr.isInvalid()) + return; if (tcr == TC_Success) Kind = CK_NoOp; diff --git a/clang/test/CXX/expr/expr.const/p2-0x.cpp b/clang/test/CXX/expr/expr.const/p2-0x.cpp index 7a43c4541ab7..d4afeb8a0318 100644 --- a/clang/test/CXX/expr/expr.const/p2-0x.cpp +++ b/clang/test/CXX/expr/expr.const/p2-0x.cpp @@ -322,7 +322,7 @@ namespace LValueToRValue { // constant expression; constexpr volatile S f() { return S(); } static_assert(f().i, ""); // ok! there's no lvalue-to-rvalue conversion here! - static_assert(((volatile const S&&)(S)0).i, ""); // expected-error {{constant expression}} + static_assert(((volatile const S&&)(S)0).i, ""); // expected-error {{constant expression}} expected-note {{read of volatile-qualified type}} } // DR1312: The proposed wording for this defect has issues, so we ignore this diff --git a/clang/test/CXX/expr/expr.post/expr.const.cast/p1-0x.cpp b/clang/test/CXX/expr/expr.post/expr.const.cast/p1-0x.cpp index 76ea96fe14ae..f4c0f1ae1229 100644 --- a/clang/test/CXX/expr/expr.post/expr.const.cast/p1-0x.cpp +++ b/clang/test/CXX/expr/expr.post/expr.const.cast/p1-0x.cpp @@ -6,14 +6,26 @@ unsigned int f(int); +struct X {}; + template T& lvalue(); template T&& xvalue(); template T prvalue(); -void test_classification(const int *ptr) { - int *ptr0 = const_cast(ptr); - int *ptr1 = const_cast(xvalue()); - int *ptr2 = const_cast(prvalue()); +void test_classification(const int *ptr, X x) { + int *&&ptr0 = const_cast(ptr); + int *&&ptr1 = const_cast(xvalue()); + int *&&ptr2 = const_cast(prvalue()); // expected-error {{const_cast from rvalue to reference type 'int *&&'}} + X &&ptr3 = const_cast(x); + X &&ptr4 = const_cast(xvalue()); + X &&ptr5 = const_cast(prvalue()); + + int *&ptr6 = const_cast(ptr); + int *&ptr7 = const_cast(xvalue()); // expected-error {{const_cast from rvalue to reference type 'int *&'}} + int *&ptr8 = const_cast(prvalue()); // expected-error {{const_cast from rvalue to reference type 'int *&'}} + X &ptr9 = const_cast(x); + X &ptrA = const_cast(xvalue()); // expected-error {{const_cast from rvalue to reference type 'X &'}} + X &ptrB = const_cast(prvalue()); // expected-error {{const_cast from rvalue to reference type 'X &'}} } struct A { diff --git a/clang/test/SemaCXX/const-cast.cpp b/clang/test/SemaCXX/const-cast.cpp index 62851f81280f..1fe350d1977a 100644 --- a/clang/test/SemaCXX/const-cast.cpp +++ b/clang/test/SemaCXX/const-cast.cpp @@ -38,6 +38,7 @@ char ***good_const_cast_test(ccvpcvpp var) f *fpp = const_cast(&fp); int const A::* const A::*icapcap = 0; int A::* A::* iapap = const_cast(icapcap); + (void)const_cast(A()); // expected-warning {{C++11}} return var4; } @@ -60,5 +61,6 @@ short *bad_const_cast_test(char const *volatile *const volatile *var) 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 {{const_cast to 'void (A::*)()', 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 &&'}} expected-warning {{C++11}} return **var3; } diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp index 173172a3abfb..b92888713e0b 100644 --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -79,6 +79,11 @@ constexpr int **n6 = const_cast(&n3); constexpr int n7 = **n5; constexpr int n8 = **n6; +// const_cast from prvalue to xvalue. +struct A { int n; }; +constexpr int n9 = (const_cast(A{123})).n; +static_assert(n9 == 123, ""); + } namespace TemplateArgumentConversion {