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.

llvm-svn: 110513
This commit is contained in:
Douglas Gregor 2010-08-07 11:51:51 +00:00
parent be05173105
commit 8b2d2fe234
15 changed files with 151 additions and 20 deletions

View File

@ -1251,7 +1251,8 @@ public:
bool areComparableObjCPointerTypes(QualType LHS, QualType RHS);
QualType areCommonBaseCompatible(const ObjCObjectPointerType *LHSOPT,
const ObjCObjectPointerType *RHSOPT);
bool canBindObjCObjectType(QualType To, QualType From);
// Functions for calculating composite types
QualType mergeTypes(QualType, QualType, bool OfBlockPointer=false,
bool Unqualified = false);

View File

@ -1927,8 +1927,12 @@ public:
CK_AnyPointerToObjCPointerCast,
/// CK_AnyPointerToBlockPointerCast - Casting any pointer to block
/// pointer
CK_AnyPointerToBlockPointerCast
CK_AnyPointerToBlockPointerCast,
/// \brief Converting between two Objective-C object types, which
/// can occur when performing reference binding to an Objective-C
/// object.
CK_ObjCObjectLValueCast
};
private:
@ -1970,6 +1974,7 @@ private:
case CK_MemberPointerToBoolean:
case CK_AnyPointerToObjCPointerCast:
case CK_AnyPointerToBlockPointerCast:
case CK_ObjCObjectLValueCast:
assert(path_empty() && "Cast kind should not have a base path!");
break;
}

View File

@ -917,6 +917,7 @@ public:
bool isObjCQualifiedInterfaceType() const; // NSString<foo>
bool isObjCQualifiedIdType() const; // id<foo>
bool isObjCQualifiedClassType() const; // Class<foo>
bool isObjCObjectOrInterfaceType() const;
bool isObjCIdType() const; // id
bool isObjCClassType() const; // Class
bool isObjCSelType() const; // Class
@ -3506,6 +3507,11 @@ inline bool Type::isObjCObjectPointerType() const {
inline bool Type::isObjCObjectType() const {
return isa<ObjCObjectType>(CanonicalType);
}
inline bool Type::isObjCObjectOrInterfaceType() const {
return isa<ObjCInterfaceType>(CanonicalType) ||
isa<ObjCObjectType>(CanonicalType);
}
inline bool Type::isObjCQualifiedIdType() const {
if (const ObjCObjectPointerType *OPT = getAs<ObjCObjectPointerType>())
return OPT->isObjCQualifiedIdType();

View File

@ -4560,6 +4560,12 @@ bool ASTContext::areComparableObjCPointerTypes(QualType LHS, QualType RHS) {
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

View File

@ -717,6 +717,8 @@ const char *CastExpr::getCastKindName() const {
return "AnyPointerToObjCPointerCast";
case CastExpr::CK_AnyPointerToBlockPointerCast:
return "AnyPointerToBlockPointerCast";
case CastExpr::CK_ObjCObjectLValueCast:
return "ObjCObjectLValueCast";
}
assert(0 && "Unhandled cast kind!");

View File

@ -2514,7 +2514,8 @@ void GRExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
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;

View File

@ -1851,6 +1851,13 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
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?");

View File

@ -925,7 +925,8 @@ Value *ScalarExprEmitter::EmitCastExpr(CastExpr *CE) {
//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 @@ Value *ScalarExprEmitter::EmitCastExpr(CastExpr *CE) {
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: {

View File

@ -4517,7 +4517,8 @@ public:
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++.

View File

@ -646,9 +646,10 @@ TryLValueToRValueCast(Sema &Self, Expr *SrcExpr, QualType DestType,
// 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;

View File

@ -2035,6 +2035,7 @@ void InitializationSequence::Step::Destroy() {
case SK_ZeroInitialization:
case SK_CAssignment:
case SK_StringInit:
case SK_ObjCObjectConversion:
break;
case SK_ConversionSequence:
@ -2201,6 +2202,13 @@ void InitializationSequence::AddStringInitStep(QualType T) {
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 @@ static OverloadingResult TryRefInitWithConversionFunction(Sema &S,
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 @@ static OverloadingResult TryRefInitWithConversionFunction(Sema &S,
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 @@ static OverloadingResult TryRefInitWithConversionFunction(Sema &S,
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 @@ static void TryReferenceInitialization(Sema &S,
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 @@ static void TryReferenceInitialization(Sema &S,
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 @@ static void TryReferenceInitialization(Sema &S,
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 @@ InitializationSequence::Perform(Sema &S,
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 @@ InitializationSequence::Perform(Sema &S,
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 @@ void InitializationSequence::dump(llvm::raw_ostream &OS) const {
case SK_StringInit:
OS << "string initialization";
break;
case SK_ObjCObjectConversion:
OS << "Objective-C object conversion";
break;
}
}
}

View File

@ -479,7 +479,10 @@ public:
/// \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 @@ public:
/// \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;

View File

@ -2575,7 +2575,8 @@ Sema::CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1,
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 @@ Sema::CompareReferenceRelationship(SourceLocation Loc,
// 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 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType,
// 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 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType,
// 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 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType,
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);

View File

@ -24,3 +24,22 @@ struct A { ~A(); };
void f(B* b) {
(void)[b getA];
}
// PR7741
@protocol P1 @end
@protocol P2 @end
@protocol P3 @end
@interface foo<P1> {} @end
@interface bar : foo <P1, P2> {} @end
typedef bar baz;
void f5(foo&);
void f5b(foo<P1>&);
void f5c(foo<P2>&);
void f5d(foo<P3>&);
void f6(baz* x) {
f5(*x);
f5b(*x);
f5c(*x);
f5d(*x);
(void)((foo&)*x);
}

View File

@ -31,3 +31,22 @@ void f3(id);
void f4(NSString &tmpstr) {
f3(&tmpstr);
}
// PR7741
@protocol P1 @end
@protocol P2 @end
@protocol P3 @end
@interface foo<P1> {} @end
@interface bar : foo <P1, P2> {} @end
typedef bar baz;
void f5(foo&);
void f5b(foo<P1>&);
void f5c(foo<P2>&);
void f5d(foo<P3>&);
void f6(baz* x) {
f5(*x);
f5b(*x);
f5c(*x);
f5d(*x);
(void)((foo&)*x);
}