Fix handling of const_cast from prvalue to rvalue reference: such a cast is

only permitted if the source object is of class type, and should materialize a
temporary for the reference to bind to.

llvm-svn: 184017
This commit is contained in:
Richard Smith 2013-06-14 22:27:52 +00:00
parent ecf9d86404
commit 82c9b5183f
5 changed files with 70 additions and 16 deletions

View File

@ -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<ReferenceType>()) {
if (isa<LValueReferenceType>(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<T2&>;
// -- a glvalue of type T1 can be explicitly converted to an xvalue of
// type T2 using the cast const_cast<T2&&>; 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<T2&&>.
if (isa<LValueReferenceType>(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<RValueReferenceType>(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;

View File

@ -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

View File

@ -6,14 +6,26 @@
unsigned int f(int);
struct X {};
template<typename T> T& lvalue();
template<typename T> T&& xvalue();
template<typename T> T prvalue();
void test_classification(const int *ptr) {
int *ptr0 = const_cast<int *&&>(ptr);
int *ptr1 = const_cast<int *&&>(xvalue<const int*>());
int *ptr2 = const_cast<int *&&>(prvalue<const int*>());
void test_classification(const int *ptr, X x) {
int *&&ptr0 = const_cast<int *&&>(ptr);
int *&&ptr1 = const_cast<int *&&>(xvalue<const int*>());
int *&&ptr2 = const_cast<int *&&>(prvalue<const int*>()); // expected-error {{const_cast from rvalue to reference type 'int *&&'}}
X &&ptr3 = const_cast<X&&>(x);
X &&ptr4 = const_cast<X&&>(xvalue<X>());
X &&ptr5 = const_cast<X&&>(prvalue<X>());
int *&ptr6 = const_cast<int *&>(ptr);
int *&ptr7 = const_cast<int *&>(xvalue<const int*>()); // expected-error {{const_cast from rvalue to reference type 'int *&'}}
int *&ptr8 = const_cast<int *&>(prvalue<const int*>()); // expected-error {{const_cast from rvalue to reference type 'int *&'}}
X &ptr9 = const_cast<X&>(x);
X &ptrA = const_cast<X&>(xvalue<X>()); // expected-error {{const_cast from rvalue to reference type 'X &'}}
X &ptrB = const_cast<X&>(prvalue<X>()); // expected-error {{const_cast from rvalue to reference type 'X &'}}
}
struct A {

View File

@ -38,6 +38,7 @@ char ***good_const_cast_test(ccvpcvpp var)
f *fpp = const_cast<f*>(&fp);
int const A::* const A::*icapcap = 0;
int A::* A::* iapap = const_cast<int A::* A::*>(icapcap);
(void)const_cast<A&&>(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<f>(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<void (A::*)()>(mfn); // expected-error {{const_cast to 'void (A::*)()', which is not a reference, pointer-to-object, or pointer-to-data-member}}
(void)const_cast<int&&>(0); // expected-error {{const_cast from rvalue to reference type 'int &&'}} expected-warning {{C++11}}
return **var3;
}

View File

@ -79,6 +79,11 @@ constexpr int **n6 = const_cast<int**>(&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&&>(A{123})).n;
static_assert(n9 == 123, "");
}
namespace TemplateArgumentConversion {