diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index df96de023df6..aa1985a88853 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -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); diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index ccbe526e0cb6..12cf2626e635 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -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; } diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 1b8412488f66..7a522f42fa33 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -917,6 +917,7 @@ public: bool isObjCQualifiedInterfaceType() const; // NSString bool isObjCQualifiedIdType() const; // id bool isObjCQualifiedClassType() const; // Class + 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(CanonicalType); } +inline bool Type::isObjCObjectOrInterfaceType() const { + return isa(CanonicalType) || + isa(CanonicalType); +} + inline bool Type::isObjCQualifiedIdType() const { if (const ObjCObjectPointerType *OPT = getAs()) return OPT->isObjCQualifiedIdType(); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 22e1ebf56970..2c16c0734836 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -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(), + getObjCObjectPointerType(From)->getAs()); +} + /// 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/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index d85162a34261..e40fc7988cbb 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -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!"); diff --git a/clang/lib/Checker/GRExprEngine.cpp b/clang/lib/Checker/GRExprEngine.cpp index ccb836fb6955..4c1838f596d4 100644 --- a/clang/lib/Checker/GRExprEngine.cpp +++ b/clang/lib/Checker/GRExprEngine.cpp @@ -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; diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 9424776898b4..e2ccf55f9375 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -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?"); diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index dbafd2bffe8e..c9e4dbd14052 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -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: { diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 3570a774b364..d22714adeddd 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -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++. diff --git a/clang/lib/Sema/SemaCXXCast.cpp b/clang/lib/Sema/SemaCXXCast.cpp index 53352661da53..4ee20a119c99 100644 --- a/clang/lib/Sema/SemaCXXCast.cpp +++ b/clang/lib/Sema/SemaCXXCast.cpp @@ -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; diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 5ffb6481206f..98a3a03bb265 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -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; } } } diff --git a/clang/lib/Sema/SemaInit.h b/clang/lib/Sema/SemaInit.h index 52481bb2564f..db0b7c3bd112 100644 --- a/clang/lib/Sema/SemaInit.h +++ b/clang/lib/Sema/SemaInit.h @@ -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; diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 27e6c2c2a346..d3855a52a13b 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -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); diff --git a/clang/test/CodeGenObjCXX/references.mm b/clang/test/CodeGenObjCXX/references.mm index c12abc7de94b..8875fd624074 100644 --- a/clang/test/CodeGenObjCXX/references.mm +++ b/clang/test/CodeGenObjCXX/references.mm @@ -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 {} @end +@interface bar : foo {} @end +typedef bar baz; +void f5(foo&); +void f5b(foo&); +void f5c(foo&); +void f5d(foo&); +void f6(baz* x) { + f5(*x); + f5b(*x); + f5c(*x); + f5d(*x); + (void)((foo&)*x); +} diff --git a/clang/test/SemaObjCXX/references.mm b/clang/test/SemaObjCXX/references.mm index 9eceeaf19f5d..a6c267ddd2f2 100644 --- a/clang/test/SemaObjCXX/references.mm +++ b/clang/test/SemaObjCXX/references.mm @@ -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 {} @end +@interface bar : foo {} @end +typedef bar baz; +void f5(foo&); +void f5b(foo&); +void f5c(foo&); +void f5d(foo&); +void f6(baz* x) { + f5(*x); + f5b(*x); + f5c(*x); + f5d(*x); + (void)((foo&)*x); +}