Teach CodeGenFunction::EmitCastLValue() to handle casts to an lvalue
that involve binding a reference to a pure rvalue temporary (e.g., not
a class temporary), by creating a new temporary and copying the result
there. Fixes PR6024.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@108431 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp
index 43bab9f..94fb086 100644
--- a/lib/CodeGen/CGExpr.cpp
+++ b/lib/CodeGen/CGExpr.cpp
@@ -1749,8 +1749,48 @@
/// cast from scalar to union.
LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
switch (E->getCastKind()) {
- default:
+ case CastExpr::CK_ToVoid:
return EmitUnsupportedLValue(E, "unexpected cast lvalue");
+
+ case CastExpr::CK_NoOp:
+ if (E->getSubExpr()->getType()->isRecordType() ||
+ E->getSubExpr()->isLvalue(getContext()) == Expr::LV_Valid) {
+ LValue LV = EmitLValue(E->getSubExpr());
+ if (LV.isPropertyRef()) {
+ QualType QT = E->getSubExpr()->getType();
+ RValue RV = EmitLoadOfPropertyRefLValue(LV, QT);
+ assert(!RV.isScalar() && "EmitCastLValue - scalar cast of property ref");
+ llvm::Value *V = RV.getAggregateAddr();
+ return LValue::MakeAddr(V, MakeQualifiers(QT));
+ }
+ return LV;
+ }
+ // Fall through to synthesize a temporary.
+
+ case CastExpr::CK_Unknown:
+ case CastExpr::CK_BitCast:
+ case CastExpr::CK_ArrayToPointerDecay:
+ case CastExpr::CK_FunctionToPointerDecay:
+ case CastExpr::CK_NullToMemberPointer:
+ case CastExpr::CK_IntegralToPointer:
+ case CastExpr::CK_PointerToIntegral:
+ case CastExpr::CK_VectorSplat:
+ case CastExpr::CK_IntegralCast:
+ case CastExpr::CK_IntegralToFloating:
+ case CastExpr::CK_FloatingToIntegral:
+ case CastExpr::CK_FloatingCast:
+ case CastExpr::CK_DerivedToBaseMemberPointer:
+ case CastExpr::CK_BaseToDerivedMemberPointer:
+ case CastExpr::CK_MemberPointerToBoolean:
+ case CastExpr::CK_AnyPointerToBlockPointerCast: {
+ // These casts only produce lvalues when we're binding a reference to a
+ // temporary realized from a (converted) pure rvalue. Emit the expression
+ // as a value, copy it into a temporary, and return an lvalue referring to
+ // that temporary.
+ llvm::Value *V = CreateMemTemp(E->getType(), "ref.temp");
+ EmitAnyExprToMem(E, V, false, false);
+ return LValue::MakeAddr(V, MakeQualifiers(E->getType()));
+ }
case CastExpr::CK_Dynamic: {
LValue LV = EmitLValue(E->getSubExpr());
@@ -1760,17 +1800,6 @@
MakeQualifiers(E->getType()));
}
- case CastExpr::CK_NoOp: {
- LValue LV = EmitLValue(E->getSubExpr());
- if (LV.isPropertyRef()) {
- QualType QT = E->getSubExpr()->getType();
- RValue RV = EmitLoadOfPropertyRefLValue(LV, QT);
- assert(!RV.isScalar() && "EmitCastLValue - scalar cast of property ref");
- llvm::Value *V = RV.getAggregateAddr();
- return LValue::MakeAddr(V, MakeQualifiers(QT));
- }
- return LV;
- }
case CastExpr::CK_ConstructorConversion:
case CastExpr::CK_UserDefinedConversion:
case CastExpr::CK_AnyPointerToObjCPointerCast:
@@ -1826,6 +1855,8 @@
return LValue::MakeAddr(V, MakeQualifiers(E->getType()));
}
}
+
+ llvm_unreachable("Unhandled lvalue cast kind?");
}
LValue CodeGenFunction::EmitNullInitializationLValue(
diff --git a/test/CodeGenCXX/reference-cast.cpp b/test/CodeGenCXX/reference-cast.cpp
new file mode 100644
index 0000000..49409d7
--- /dev/null
+++ b/test/CodeGenCXX/reference-cast.cpp
@@ -0,0 +1,170 @@
+// RUN: %clang_cc1 -emit-llvm -triple x86_64-apple-darwin10 -o - %s | FileCheck %s
+
+// PR6024
+extern int i;
+
+// CHECK: define i32* @_Z16lvalue_noop_castv() nounwind
+const int &lvalue_noop_cast() {
+ if (i == 0)
+ // CHECK: store i32 17, i32*
+ return (const int&)17;
+ else if (i == 1)
+ // CHECK: store i32 17, i32*
+ return static_cast<const int&>(17);
+ // CHECK: store i32 17, i32*
+ return 17;
+}
+
+// CHECK: define i16* @_Z20lvalue_integral_castv()
+const short &lvalue_integral_cast() {
+ if (i == 0)
+ // CHECK: store i16 17, i16*
+ return (const short&)17;
+ else if (i == 1)
+ // CHECK: store i16 17, i16*
+ return static_cast<const short&>(17);
+ // CHECK: store i16 17, i16*
+ return 17;
+}
+
+// CHECK: define i16* @_Z29lvalue_floating_integral_castv()
+const short &lvalue_floating_integral_cast() {
+ if (i == 0)
+ // CHECK: store i16 17, i16*
+ return (const short&)17.5;
+ else if (i == 1)
+ // CHECK: store i16 17, i16*
+ return static_cast<const short&>(17.5);
+ // CHECK: store i16 17, i16*
+ return 17.5;
+}
+
+// CHECK: define float* @_Z29lvalue_integral_floating_castv()
+const float &lvalue_integral_floating_cast() {
+ if (i == 0)
+ // CHECK: store float 1.700000e+01, float*
+ return (const float&)17;
+ else if (i == 1)
+ // CHECK: store float 1.700000e+01, float*
+ return static_cast<const float&>(17);
+ // CHECK: store float 1.700000e+01, float*
+ return 17;
+}
+
+// CHECK: define float* @_Z20lvalue_floating_castv()
+const float &lvalue_floating_cast() {
+ if (i == 0)
+ // CHECK: store float 1.700000e+01, float*
+ return (const float&)17.0;
+ else if (i == 1)
+ // CHECK: store float 1.700000e+01, float*
+ return static_cast<const float&>(17.0);
+ // CHECK: store float 1.700000e+01, float*
+ return 17.0;
+}
+
+int get_int();
+
+// CHECK: define i8* @_Z24lvalue_integer_bool_castv()
+const bool &lvalue_integer_bool_cast() {
+ if (i == 0)
+ // CHECK: call i32 @_Z7get_intv()
+ // CHECK: store i8
+ return (const bool&)get_int();
+ else if (i == 1)
+ // CHECK: call i32 @_Z7get_intv()
+ // CHECK: store i8
+ return static_cast<const bool&>(get_int());
+ // CHECK: call i32 @_Z7get_intv()
+ // CHECK: store i8
+ return get_int();
+}
+
+float get_float();
+
+// CHECK: define i8* @_Z25lvalue_floating_bool_castv()
+const bool &lvalue_floating_bool_cast() {
+ if (i == 0)
+ // CHECK: call float @_Z9get_floatv()
+ // CHECK: fcmp une float
+ // CHECK: store i8
+ return (const bool&)get_float();
+ else if (i == 1)
+ // CHECK: call float @_Z9get_floatv()
+ // CHECK: fcmp une float
+ // CHECK: store i8
+ return static_cast<const bool&>(get_float());
+ // CHECK: call float @_Z9get_floatv()
+ // CHECK: fcmp une float
+ // CHECK: store i8
+ return get_float();
+}
+
+struct X { };
+typedef int X::*pm;
+typedef int (X::*pmf)(int);
+
+pm get_pointer_to_member_data();
+pmf get_pointer_to_member_function();
+
+// CHECK: define i8* @_Z26lvalue_ptrmem_to_bool_castv()
+const bool &lvalue_ptrmem_to_bool_cast() {
+ if (i == 0)
+ // CHECK: call i64 @_Z26get_pointer_to_member_datav()
+ // CHECK: store i8
+ // CHECK: store i8*
+ return (const bool&)get_pointer_to_member_data();
+ else if (i == 1)
+ // CHECK: call i64 @_Z26get_pointer_to_member_datav()
+ // CHECK: store i8
+ // CHECK: store i8*
+ return static_cast<const bool&>(get_pointer_to_member_data());
+ // CHECK: call i64 @_Z26get_pointer_to_member_datav()
+ // CHECK: store i8
+ // CHECK: store i8*
+ return get_pointer_to_member_data();
+}
+
+// CHECK: define i8* @_Z27lvalue_ptrmem_to_bool_cast2v
+const bool &lvalue_ptrmem_to_bool_cast2() {
+ if (i == 0)
+ // CHECK: {{call.*_Z30get_pointer_to_member_functionv}}
+ // CHECK: store i8
+ // CHECK: store i8*
+ return (const bool&)get_pointer_to_member_function();
+ else if (i == 1)
+ // CHECK: {{call.*_Z30get_pointer_to_member_functionv}}
+ // CHECK: store i8
+ // CHECK: store i8*
+ return static_cast<const bool&>(get_pointer_to_member_function());
+ // CHECK: {{call.*_Z30get_pointer_to_member_functionv}}
+ // CHECK: store i8
+ // CHECK: store i8*
+ return get_pointer_to_member_function();
+}
+
+_Complex double get_complex_double();
+
+// CHECK: {{define.*_Z2f1v}}
+const _Complex float &f1() {
+ if (i == 0)
+ // CHECK: {{call.*_Z18get_complex_doublev}}
+ // CHECK: fptrunc
+ // CHECK: fptrunc
+ // CHECK: store float
+ // CHECK: store float
+ return (const _Complex float&)get_complex_double();
+ else if (i == 1)
+ // CHECK: {{call.*_Z18get_complex_doublev}}
+ // CHECK: fptrunc
+ // CHECK: fptrunc
+ // CHECK: store float
+ // CHECK: store float
+ return static_cast<const _Complex float&>(get_complex_double());
+ // CHECK: {{call.*_Z18get_complex_doublev}}
+ // CHECK: fptrunc
+ // CHECK: fptrunc
+ // CHECK: store float
+ // CHECK: store float
+ return get_complex_double();
+}