From: Douglas Gregor Date: Fri, 19 Dec 2008 17:40:08 +0000 (+0000) Subject: Allow downcasts of pointers to Objective-C interfaces, with a X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=45920e82bd87454545642ee2612e656a4140889d;p=clang Allow downcasts of pointers to Objective-C interfaces, with a warning. This matches GCC's behavior and addresses . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@61246 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 0534f3dce2..1fac77549b 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -382,7 +382,7 @@ public: bool IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType); bool IsFloatingPointPromotion(QualType FromType, QualType ToType); bool IsPointerConversion(Expr *From, QualType FromType, QualType ToType, - QualType& ConvertedType); + QualType& ConvertedType, bool &IncompatibleObjC); bool CheckPointerConversion(Expr *From, QualType ToType); bool IsQualificationConversion(QualType FromType, QualType ToType); bool IsUserDefinedConversion(Expr *From, QualType ToType, @@ -1260,9 +1260,11 @@ public: bool IsStringLiteralToNonConstPointerConversion(Expr *From, QualType ToType); - bool PerformImplicitConversion(Expr *&From, QualType ToType); + bool PerformImplicitConversion(Expr *&From, QualType ToType, + const char *Flavor); bool PerformImplicitConversion(Expr *&From, QualType ToType, - const StandardConversionSequence& SCS); + const StandardConversionSequence& SCS, + const char *Flavor); /// the following "Check" methods will return a valid/converted QualType /// or a null QualType (indicating an error diagnostic was issued). diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 925a7bb29b..6c1af5f878 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -744,7 +744,10 @@ bool Sema::CheckSingleInitializer(Expr *&Init, QualType DeclType) { // Get the type before calling CheckSingleAssignmentConstraints(), since // it can promote the expression. QualType InitType = Init->getType(); - + + if (getLangOptions().CPlusPlus) + return PerformCopyInitialization(Init, DeclType, "initializing"); + AssignConvertType ConvTy = CheckSingleAssignmentConstraints(DeclType, Init); return DiagnoseAssignmentResult(ConvTy, Init->getLocStart(), DeclType, InitType, Init, "initializing"); @@ -843,7 +846,7 @@ bool Sema::CheckInitializerTypes(Expr *&Init, QualType &DeclType, // destination type. // FIXME: We're pretending to do copy elision here; return to // this when we have ASTs for such things. - if (!PerformImplicitConversion(Init, DeclType)) + if (!PerformImplicitConversion(Init, DeclType, "initializing")) return false; return Diag(InitLoc, diag::err_typecheck_convert_incompatible) diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 7be76bba1a..f2f02e2125 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1838,7 +1838,7 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, *ICS = TryImplicitConversion(Init, T1, SuppressUserConversions); return ICS->ConversionKind == ImplicitConversionSequence::BadConversion; } else { - return PerformImplicitConversion(Init, T1); + return PerformImplicitConversion(Init, T1, "initializing"); } } diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index d31855e36b..da0354886c 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -2138,7 +2138,8 @@ Sema::CheckSingleAssignmentConstraints(QualType lhsType, Expr *&rExpr) { // C++ 5.17p3: If the left operand is not of class type, the // expression is implicitly converted (C++ 4) to the // cv-unqualified type of the left operand. - if (PerformImplicitConversion(rExpr, lhsType.getUnqualifiedType())) + if (PerformImplicitConversion(rExpr, lhsType.getUnqualifiedType(), + "assigning")) return Incompatible; else return Compatible; diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index e951016361..e18f1437ab 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -708,14 +708,16 @@ Sema::IsStringLiteralToNonConstPointerConversion(Expr *From, QualType ToType) { /// PerformImplicitConversion - Perform an implicit conversion of the /// expression From to the type ToType. Returns true if there was an /// error, false otherwise. The expression From is replaced with the -/// converted expression. +/// converted expression. Flavor is the kind of conversion we're +/// performing, used in the error message. bool -Sema::PerformImplicitConversion(Expr *&From, QualType ToType) +Sema::PerformImplicitConversion(Expr *&From, QualType ToType, + const char *Flavor) { ImplicitConversionSequence ICS = TryImplicitConversion(From, ToType); switch (ICS.ConversionKind) { case ImplicitConversionSequence::StandardConversion: - if (PerformImplicitConversion(From, ToType, ICS.Standard)) + if (PerformImplicitConversion(From, ToType, ICS.Standard, Flavor)) return true; break; @@ -742,10 +744,12 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType) /// expression From to the type ToType by following the standard /// conversion sequence SCS. Returns true if there was an error, false /// otherwise. The expression From is replaced with the converted -/// expression. +/// expression. Flavor is the context in which we're performing this +/// conversion, for use in error messages. bool Sema::PerformImplicitConversion(Expr *&From, QualType ToType, - const StandardConversionSequence& SCS) + const StandardConversionSequence& SCS, + const char *Flavor) { // Overall FIXME: we are recomputing too many types here and doing // far too much extra work. What this means is that we need to keep @@ -808,6 +812,14 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType, break; case ICK_Pointer_Conversion: + if (SCS.IncompatibleObjC) { + // Diagnose incompatible Objective-C conversions + Diag(From->getSourceRange().getBegin(), + diag::ext_typecheck_convert_incompatible_pointer) + << From->getType() << ToType << Flavor + << From->getSourceRange(); + } + if (CheckPointerConversion(From, ToType)) return true; ImpCastExprToType(From, ToType); diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 3671711964..f03c7a4e90 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -419,6 +419,7 @@ Sema::IsStandardConversion(Expr* From, QualType ToType, // Standard conversions (C++ [conv]) SCS.setAsIdentityConversion(); SCS.Deprecated = false; + SCS.IncompatibleObjC = false; SCS.FromTypePtr = FromType.getAsOpaquePtr(); SCS.CopyConstructor = 0; @@ -494,6 +495,7 @@ Sema::IsStandardConversion(Expr* From, QualType ToType, // point promotion, integral conversion, floating point conversion, // floating-integral conversion, pointer conversion, // pointer-to-member conversion, or boolean conversion (C++ 4p1). + bool IncompatibleObjC = false; if (Context.getCanonicalType(FromType).getUnqualifiedType() == Context.getCanonicalType(ToType).getUnqualifiedType()) { // The unqualified versions of the types are the same: there's no @@ -533,8 +535,10 @@ Sema::IsStandardConversion(Expr* From, QualType ToType, FromType = ToType.getUnqualifiedType(); } // Pointer conversions (C++ 4.10). - else if (IsPointerConversion(From, FromType, ToType, FromType)) { + else if (IsPointerConversion(From, FromType, ToType, FromType, + IncompatibleObjC)) { SCS.Second = ICK_Pointer_Conversion; + SCS.IncompatibleObjC = IncompatibleObjC; } // FIXME: Pointer to member conversions (4.11). // Boolean conversions (C++ 4.12). @@ -751,10 +755,15 @@ BuildSimilarlyQualifiedPointerType(const PointerType *FromPtr, /// appropriate overloading rules for Objective-C, we may want to /// split the Objective-C checks into a different routine; however, /// GCC seems to consider all of these conversions to be pointer -/// conversions, so for now they live here. +/// conversions, so for now they live here. IncompatibleObjC will be +/// set if the conversion is an allowed Objective-C conversion that +/// should result in a warning. bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType, - QualType& ConvertedType) + QualType& ConvertedType, + bool &IncompatibleObjC) { + IncompatibleObjC = false; + // Blocks: Block pointers can be converted to void*. if (FromType->isBlockPointerType() && ToType->isPointerType() && ToType->getAsPointerType()->getPointeeType()->isVoidType()) { @@ -836,6 +845,18 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType, return true; } + if (FromIface && ToIface && + Context.canAssignObjCInterfaces(FromIface, ToIface)) { + // Okay: this is some kind of implicit downcast of Objective-C + // interfaces, which is permitted. However, we're going to + // complain about it. + IncompatibleObjC = true; + ConvertedType = BuildSimilarlyQualifiedPointerType(FromTypePtr, + ToPointeeType, + ToType, Context); + return true; + } + // Objective C++: We're able to convert between "id" and a pointer // to any interface (in both directions). if ((FromIface && Context.isObjCIdType(ToPointeeType)) @@ -1533,7 +1554,7 @@ bool Sema::PerformCopyInitialization(Expr *&From, QualType ToType, if (ToType->isReferenceType()) return CheckReferenceInit(From, ToType); - if (!PerformImplicitConversion(From, ToType)) + if (!PerformImplicitConversion(From, ToType, Flavor)) return false; return Diag(From->getSourceRange().getBegin(), diff --git a/lib/Sema/SemaOverload.h b/lib/Sema/SemaOverload.h index 13be00ad7f..c7bf8b2d6d 100644 --- a/lib/Sema/SemaOverload.h +++ b/lib/Sema/SemaOverload.h @@ -98,6 +98,10 @@ namespace clang { /// (C++ 4.2p2). bool Deprecated : 1; + /// IncompatibleObjC - Whether this is an Objective-C conversion + /// that we should warn about (if we actually use it). + bool IncompatibleObjC : 1; + /// ReferenceBinding - True when this is a reference binding /// (C++ [over.ics.ref]). bool ReferenceBinding : 1; diff --git a/test/SemaObjCXX/overload.mm b/test/SemaObjCXX/overload.mm index 16a36fc58b..0accb46ac0 100644 --- a/test/SemaObjCXX/overload.mm +++ b/test/SemaObjCXX/overload.mm @@ -45,6 +45,11 @@ void test(A* a, B* b, id val) { // int& i3 = h(b); FIXME: we match GCC here, but shouldn't this work? } +void downcast_test(A* a) { + B* b = a; // expected-warning{{incompatible pointer types initializing 'B *', expected 'A *'}} + b = a; // expected-warning{{incompatible pointer types assigning 'B *', expected 'A *'}} +} + int& cv(A*); float& cv(const A*); int& cv2(void*);