Allow reference binding of a reference of Objective-C object type to
an lvalue of another, compatible Objective-C object type (e.g., a
subclass). Introduce a new initialization sequence step kind to
describe this binding, along with a new cast kind. Fixes PR7741.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@110513 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 22e1ebf..2c16c07 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -4560,6 +4560,12 @@
canAssignObjCInterfaces(RHSOPT, LHSOPT);
}
+bool ASTContext::canBindObjCObjectType(QualType To, QualType From) {
+ return canAssignObjCInterfaces(
+ getObjCObjectPointerType(To)->getAs<ObjCObjectPointerType>(),
+ getObjCObjectPointerType(From)->getAs<ObjCObjectPointerType>());
+}
+
/// typesAreCompatible - C99 6.7.3p9: For two qualified types to be compatible,
/// both shall have the identically qualified version of a compatible type.
/// C99 6.2.7p1: Two types have compatible types if their types are the
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index d85162a..e40fc79 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -717,6 +717,8 @@
return "AnyPointerToObjCPointerCast";
case CastExpr::CK_AnyPointerToBlockPointerCast:
return "AnyPointerToBlockPointerCast";
+ case CastExpr::CK_ObjCObjectLValueCast:
+ return "ObjCObjectLValueCast";
}
assert(0 && "Unhandled cast kind!");
diff --git a/lib/Checker/GRExprEngine.cpp b/lib/Checker/GRExprEngine.cpp
index ccb836f..4c1838f 100644
--- a/lib/Checker/GRExprEngine.cpp
+++ b/lib/Checker/GRExprEngine.cpp
@@ -2514,7 +2514,8 @@
case CastExpr::CK_AnyPointerToObjCPointerCast:
case CastExpr::CK_AnyPointerToBlockPointerCast:
case CastExpr::CK_DerivedToBase:
- case CastExpr::CK_UncheckedDerivedToBase: {
+ case CastExpr::CK_UncheckedDerivedToBase:
+ case CastExpr::CK_ObjCObjectLValueCast: {
// Delegate to SValuator to process.
for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) {
ExplodedNode* N = *I;
diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp
index 9424776..e2ccf55 100644
--- a/lib/CodeGen/CGExpr.cpp
+++ b/lib/CodeGen/CGExpr.cpp
@@ -1851,6 +1851,13 @@
ConvertType(CE->getTypeAsWritten()));
return LValue::MakeAddr(V, MakeQualifiers(E->getType()));
}
+ case CastExpr::CK_ObjCObjectLValueCast: {
+ LValue LV = EmitLValue(E->getSubExpr());
+ QualType ToType = getContext().getLValueReferenceType(E->getType());
+ llvm::Value *V = Builder.CreateBitCast(LV.getAddress(),
+ ConvertType(ToType));
+ return LValue::MakeAddr(V, MakeQualifiers(E->getType()));
+ }
}
llvm_unreachable("Unhandled lvalue cast kind?");
diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp
index dbafd2b..c9e4dbd 100644
--- a/lib/CodeGen/CGExprScalar.cpp
+++ b/lib/CodeGen/CGExprScalar.cpp
@@ -925,7 +925,8 @@
//assert(0 && "Unknown cast kind!");
break;
- case CastExpr::CK_LValueBitCast: {
+ case CastExpr::CK_LValueBitCast:
+ case CastExpr::CK_ObjCObjectLValueCast: {
Value *V = EmitLValue(E).getAddress();
V = Builder.CreateBitCast(V,
ConvertType(CGF.getContext().getPointerType(DestTy)));
@@ -1044,7 +1045,10 @@
return Builder.CreatePtrToInt(Src, ConvertType(DestTy));
}
case CastExpr::CK_ToVoid: {
- CGF.EmitAnyExpr(E, 0, false, true);
+ if (E->Classify(CGF.getContext()).isGLValue())
+ CGF.EmitLValue(E);
+ else
+ CGF.EmitAnyExpr(E, 0, false, true);
return 0;
}
case CastExpr::CK_VectorSplat: {
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 3570a77..d22714a 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -4517,7 +4517,8 @@
ReferenceCompareResult CompareReferenceRelationship(SourceLocation Loc,
QualType T1, QualType T2,
- bool& DerivedToBase);
+ bool &DerivedToBase,
+ bool &ObjCConversion);
/// CheckCastTypes - Check type constraints for casting between types under
/// C semantics, or forward to CXXCheckCStyleCast in C++.
diff --git a/lib/Sema/SemaCXXCast.cpp b/lib/Sema/SemaCXXCast.cpp
index 5335266..4ee20a1 100644
--- a/lib/Sema/SemaCXXCast.cpp
+++ b/lib/Sema/SemaCXXCast.cpp
@@ -646,9 +646,10 @@
// this is the only cast possibility, so we issue an error if we fail now.
// FIXME: Should allow casting away constness if CStyle.
bool DerivedToBase;
+ bool ObjCConversion;
if (Self.CompareReferenceRelationship(SrcExpr->getLocStart(),
SrcExpr->getType(), R->getPointeeType(),
- DerivedToBase) <
+ DerivedToBase, ObjCConversion) <
Sema::Ref_Compatible_With_Added_Qualification) {
msg = diag::err_bad_lvalue_to_rvalue_cast;
return TC_Failed;
diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp
index 5ffb648..98a3a03 100644
--- a/lib/Sema/SemaInit.cpp
+++ b/lib/Sema/SemaInit.cpp
@@ -2035,6 +2035,7 @@
case SK_ZeroInitialization:
case SK_CAssignment:
case SK_StringInit:
+ case SK_ObjCObjectConversion:
break;
case SK_ConversionSequence:
@@ -2201,6 +2202,13 @@
Steps.push_back(S);
}
+void InitializationSequence::AddObjCObjectConversionStep(QualType T) {
+ Step S;
+ S.Kind = SK_ObjCObjectConversion;
+ S.Type = T;
+ Steps.push_back(S);
+}
+
void InitializationSequence::SetOverloadFailure(FailureKind Failure,
OverloadingResult Result) {
SequenceKind = FailedSequence;
@@ -2275,10 +2283,13 @@
QualType T2 = cv2T2.getUnqualifiedType();
bool DerivedToBase;
+ bool ObjCConversion;
assert(!S.CompareReferenceRelationship(Initializer->getLocStart(),
- T1, T2, DerivedToBase) &&
+ T1, T2, DerivedToBase,
+ ObjCConversion) &&
"Must have incompatible references when binding via conversion");
(void)DerivedToBase;
+ (void)ObjCConversion;
// Build the candidate set directly in the initialization sequence
// structure, so that it will persist if we fail.
@@ -2400,10 +2411,11 @@
ImplicitCastExpr::LValue : ImplicitCastExpr::XValue;
bool NewDerivedToBase = false;
+ bool NewObjCConversion = false;
Sema::ReferenceCompareResult NewRefRelationship
= S.CompareReferenceRelationship(DeclLoc, T1,
T2.getNonLValueExprType(S.Context),
- NewDerivedToBase);
+ NewDerivedToBase, NewObjCConversion);
if (NewRefRelationship == Sema::Ref_Incompatible) {
// If the type we've converted to is not reference-related to the
// type we're looking for, then there is another conversion step
@@ -2418,8 +2430,12 @@
Sequence.AddDerivedToBaseCastStep(
S.Context.getQualifiedType(T1,
T2.getNonReferenceType().getQualifiers()),
- Category);
-
+ Category);
+ else if (NewObjCConversion)
+ Sequence.AddObjCObjectConversionStep(
+ S.Context.getQualifiedType(T1,
+ T2.getNonReferenceType().getQualifiers()));
+
if (cv1T1.getQualifiers() != T2.getNonReferenceType().getQualifiers())
Sequence.AddQualificationConversionStep(cv1T1, Category);
@@ -2467,9 +2483,11 @@
bool isLValueRef = DestType->isLValueReferenceType();
bool isRValueRef = !isLValueRef;
bool DerivedToBase = false;
+ bool ObjCConversion = false;
Expr::Classification InitCategory = Initializer->Classify(S.Context);
Sema::ReferenceCompareResult RefRelationship
- = S.CompareReferenceRelationship(DeclLoc, cv1T1, cv2T2, DerivedToBase);
+ = S.CompareReferenceRelationship(DeclLoc, cv1T1, cv2T2, DerivedToBase,
+ ObjCConversion);
// C++0x [dcl.init.ref]p5:
// A reference to type "cv1 T1" is initialized by an expression of type
@@ -2497,6 +2515,10 @@
Sequence.AddDerivedToBaseCastStep(
S.Context.getQualifiedType(T1, T2Quals),
ImplicitCastExpr::LValue);
+ else if (ObjCConversion)
+ Sequence.AddObjCObjectConversionStep(
+ S.Context.getQualifiedType(T1, T2Quals));
+
if (T1Quals != T2Quals)
Sequence.AddQualificationConversionStep(cv1T1,ImplicitCastExpr::LValue);
bool BindingTemporary = T1Quals.hasConst() && !T1Quals.hasVolatile() &&
@@ -2577,6 +2599,10 @@
S.Context.getQualifiedType(T1, T2Quals),
isXValue ? ImplicitCastExpr::XValue
: ImplicitCastExpr::RValue);
+ else if (ObjCConversion)
+ Sequence.AddObjCObjectConversionStep(
+ S.Context.getQualifiedType(T1, T2Quals));
+
if (T1Quals != T2Quals)
Sequence.AddQualificationConversionStep(cv1T1,
isXValue ? ImplicitCastExpr::XValue
@@ -3546,6 +3572,7 @@
case SK_ListInitialization:
case SK_CAssignment:
case SK_StringInit:
+ case SK_ObjCObjectConversion:
assert(Args.size() == 1);
CurInit = Sema::OwningExprResult(S, ((Expr **)(Args.get()))[0]->Retain());
if (CurInit.isInvalid())
@@ -3926,6 +3953,14 @@
CheckStringInit(CurInitExpr, ResultType ? *ResultType : Ty, S);
break;
}
+
+ case SK_ObjCObjectConversion:
+ S.ImpCastExprToType(CurInitExpr, Step->Type,
+ CastExpr::CK_ObjCObjectLValueCast,
+ S.CastCategory(CurInitExpr));
+ CurInit.release();
+ CurInit = S.Owned(CurInitExpr);
+ break;
}
}
@@ -4392,6 +4427,10 @@
case SK_StringInit:
OS << "string initialization";
break;
+
+ case SK_ObjCObjectConversion:
+ OS << "Objective-C object conversion";
+ break;
}
}
}
diff --git a/lib/Sema/SemaInit.h b/lib/Sema/SemaInit.h
index 52481bb..db0b7c3 100644
--- a/lib/Sema/SemaInit.h
+++ b/lib/Sema/SemaInit.h
@@ -479,7 +479,10 @@
/// \brief C assignment
SK_CAssignment,
/// \brief Initialization by string
- SK_StringInit
+ SK_StringInit,
+ /// \brief An initialization that "converts" an Objective-C object
+ /// (not a point to an object) to another Objective-C object type.
+ SK_ObjCObjectConversion
};
/// \brief A single step in the initialization sequence.
@@ -737,6 +740,10 @@
/// \brief Add a string init step.
void AddStringInitStep(QualType T);
+ /// \brief Add an Objective-C object conversion step, which is
+ /// always a no-op.
+ void AddObjCObjectConversionStep(QualType T);
+
/// \brief Note that this initialization sequence failed.
void SetFailed(FailureKind Failure) {
SequenceKind = FailedSequence;
diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp
index 27e6c2c..d3855a5 100644
--- a/lib/Sema/SemaOverload.cpp
+++ b/lib/Sema/SemaOverload.cpp
@@ -2575,7 +2575,8 @@
Sema::ReferenceCompareResult
Sema::CompareReferenceRelationship(SourceLocation Loc,
QualType OrigT1, QualType OrigT2,
- bool& DerivedToBase) {
+ bool &DerivedToBase,
+ bool &ObjCConversion) {
assert(!OrigT1->isReferenceType() &&
"T1 must be the pointee type of the reference type");
assert(!OrigT2->isReferenceType() && "T2 cannot be a reference type");
@@ -2590,11 +2591,17 @@
// Given types "cv1 T1" and "cv2 T2," "cv1 T1" is
// reference-related to "cv2 T2" if T1 is the same type as T2, or
// T1 is a base class of T2.
- if (UnqualT1 == UnqualT2)
- DerivedToBase = false;
- else if (!RequireCompleteType(Loc, OrigT2, PDiag()) &&
+ DerivedToBase = false;
+ ObjCConversion = false;
+ if (UnqualT1 == UnqualT2) {
+ // Nothing to do.
+ } else if (!RequireCompleteType(Loc, OrigT2, PDiag()) &&
IsDerivedFrom(UnqualT2, UnqualT1))
DerivedToBase = true;
+ else if (UnqualT1->isObjCObjectOrInterfaceType() &&
+ UnqualT2->isObjCObjectOrInterfaceType() &&
+ Context.canBindObjCObjectType(UnqualT1, UnqualT2))
+ ObjCConversion = true;
else
return Ref_Incompatible;
@@ -2741,9 +2748,11 @@
// Compute some basic properties of the types and the initializer.
bool isRValRef = DeclType->isRValueReferenceType();
bool DerivedToBase = false;
+ bool ObjCConversion = false;
Expr::Classification InitCategory = Init->Classify(S.Context);
Sema::ReferenceCompareResult RefRelationship
- = S.CompareReferenceRelationship(DeclLoc, T1, T2, DerivedToBase);
+ = S.CompareReferenceRelationship(DeclLoc, T1, T2, DerivedToBase,
+ ObjCConversion);
// C++0x [dcl.init.ref]p5:
@@ -2769,7 +2778,9 @@
// derived-to-base Conversion (13.3.3.1).
ICS.setStandard();
ICS.Standard.First = ICK_Identity;
- ICS.Standard.Second = DerivedToBase? ICK_Derived_To_Base : ICK_Identity;
+ ICS.Standard.Second = DerivedToBase? ICK_Derived_To_Base
+ : ObjCConversion? ICK_Compatible_Conversion
+ : ICK_Identity;
ICS.Standard.Third = ICK_Identity;
ICS.Standard.FromTypePtr = T2.getAsOpaquePtr();
ICS.Standard.setToType(0, T2);
@@ -2857,7 +2868,9 @@
RefRelationship >= Sema::Ref_Compatible_With_Added_Qualification) {
ICS.setStandard();
ICS.Standard.First = ICK_Identity;
- ICS.Standard.Second = DerivedToBase? ICK_Derived_To_Base : ICK_Identity;
+ ICS.Standard.Second = DerivedToBase? ICK_Derived_To_Base
+ : ObjCConversion? ICK_Compatible_Conversion
+ : ICK_Identity;
ICS.Standard.Third = ICK_Identity;
ICS.Standard.FromTypePtr = T2.getAsOpaquePtr();
ICS.Standard.setToType(0, T2);