From: Douglas Gregor Date: Fri, 19 Dec 2008 19:13:09 +0000 (+0000) Subject: Support more implicit conversions for Objective-C types. Addresses . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@61255 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 1fac77549b..56b681c070 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -383,6 +383,8 @@ public: bool IsFloatingPointPromotion(QualType FromType, QualType ToType); bool IsPointerConversion(Expr *From, QualType FromType, QualType ToType, QualType& ConvertedType, bool &IncompatibleObjC); + bool isObjCPointerConversion(QualType FromType, QualType ToType, + QualType& ConvertedType, bool &IncompatibleObjC); bool CheckPointerConversion(Expr *From, QualType ToType); bool IsQualificationConversion(QualType FromType, QualType ToType); bool IsUserDefinedConversion(Expr *From, QualType ToType, diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index f03c7a4e90..c719f56c4e 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -763,6 +763,8 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType, bool &IncompatibleObjC) { IncompatibleObjC = false; + if (isObjCPointerConversion(FromType, ToType, ConvertedType, IncompatibleObjC)) + return true; // Blocks: Block pointers can be converted to void*. if (FromType->isBlockPointerType() && ToType->isPointerType() && @@ -777,13 +779,6 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType, return true; } - // Conversions with Objective-C's id<...>. - if ((FromType->isObjCQualifiedIdType() || ToType->isObjCQualifiedIdType()) && - ObjCQualifiedIdTypesAreCompatible(ToType, FromType, /*compare=*/false)) { - ConvertedType = ToType; - return true; - } - const PointerType* ToTypePtr = ToType->getAsPointerType(); if (!ToTypePtr) return false; @@ -805,7 +800,8 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType, // An rvalue of type "pointer to cv T," where T is an object type, // can be converted to an rvalue of type "pointer to cv void" (C++ // 4.10p2). - if (FromPointeeType->isIncompleteOrObjectType() && ToPointeeType->isVoidType()) { + if (FromPointeeType->isIncompleteOrObjectType() && + ToPointeeType->isVoidType()) { ConvertedType = BuildSimilarlyQualifiedPointerType(FromTypePtr, ToPointeeType, ToType, Context); @@ -833,6 +829,37 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType, return true; } + return false; +} + +/// isObjCPointerConversion - Determines whether this is an +/// Objective-C pointer conversion. Subroutine of IsPointerConversion, +/// with the same arguments and return values. +bool Sema::isObjCPointerConversion(QualType FromType, QualType ToType, + QualType& ConvertedType, + bool &IncompatibleObjC) { + if (!getLangOptions().ObjC1) + return false; + + // Conversions with Objective-C's id<...>. + if ((FromType->isObjCQualifiedIdType() || ToType->isObjCQualifiedIdType()) && + ObjCQualifiedIdTypesAreCompatible(ToType, FromType, /*compare=*/false)) { + ConvertedType = ToType; + return true; + } + + const PointerType* ToTypePtr = ToType->getAsPointerType(); + if (!ToTypePtr) + return false; + + // Beyond this point, both types need to be pointers. + const PointerType *FromTypePtr = FromType->getAsPointerType(); + if (!FromTypePtr) + return false; + + QualType FromPointeeType = FromTypePtr->getPointeeType(); + QualType ToPointeeType = ToTypePtr->getPointeeType(); + // Objective C++: We're able to convert from a pointer to an // interface to a pointer to a different interface. const ObjCInterfaceType* FromIface = FromPointeeType->getAsObjCInterfaceType(); @@ -877,7 +904,81 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType, return true; } - return false; + // If we have pointers to pointers, recursively check whether this + // is an Objective-C conversion. + if (FromPointeeType->isPointerType() && ToPointeeType->isPointerType() && + isObjCPointerConversion(FromPointeeType, ToPointeeType, ConvertedType, + IncompatibleObjC)) { + // We always complain about this conversion. + IncompatibleObjC = true; + ConvertedType = ToType; + return true; + } + + // If we have pointers to functions, check whether the only + // differences in the argument and result types are in Objective-C + // pointer conversions. If so, we permit the conversion (but + // complain about it). + const FunctionTypeProto *FromFunctionType + = FromPointeeType->getAsFunctionTypeProto(); + const FunctionTypeProto *ToFunctionType + = ToPointeeType->getAsFunctionTypeProto(); + if (FromFunctionType && ToFunctionType) { + // If the function types are exactly the same, this isn't an + // Objective-C pointer conversion. + if (Context.getCanonicalType(FromPointeeType) + == Context.getCanonicalType(ToPointeeType)) + return false; + + // Perform the quick checks that will tell us whether these + // function types are obviously different. + if (FromFunctionType->getNumArgs() != ToFunctionType->getNumArgs() || + FromFunctionType->isVariadic() != ToFunctionType->isVariadic() || + FromFunctionType->getTypeQuals() != ToFunctionType->getTypeQuals()) + return false; + + bool HasObjCConversion = false; + if (Context.getCanonicalType(FromFunctionType->getResultType()) + == Context.getCanonicalType(ToFunctionType->getResultType())) { + // Okay, the types match exactly. Nothing to do. + } else if (isObjCPointerConversion(FromFunctionType->getResultType(), + ToFunctionType->getResultType(), + ConvertedType, IncompatibleObjC)) { + // Okay, we have an Objective-C pointer conversion. + HasObjCConversion = true; + } else { + // Function types are too different. Abort. + return false; + } + + // Check argument types. + for (unsigned ArgIdx = 0, NumArgs = FromFunctionType->getNumArgs(); + ArgIdx != NumArgs; ++ArgIdx) { + QualType FromArgType = FromFunctionType->getArgType(ArgIdx); + QualType ToArgType = ToFunctionType->getArgType(ArgIdx); + if (Context.getCanonicalType(FromArgType) + == Context.getCanonicalType(ToArgType)) { + // Okay, the types match exactly. Nothing to do. + } else if (isObjCPointerConversion(FromArgType, ToArgType, + ConvertedType, IncompatibleObjC)) { + // Okay, we have an Objective-C pointer conversion. + HasObjCConversion = true; + } else { + // Argument types are too different. Abort. + return false; + } + } + + if (HasObjCConversion) { + // We had an Objective-C conversion. Allow this pointer + // conversion, but complain about it. + ConvertedType = ToType; + IncompatibleObjC = true; + return true; + } + } + + return false; } /// CheckPointerConversion - Check the pointer conversion from the diff --git a/test/SemaObjCXX/overload.mm b/test/SemaObjCXX/overload.mm index 0accb46ac0..41865ff3e8 100644 --- a/test/SemaObjCXX/overload.mm +++ b/test/SemaObjCXX/overload.mm @@ -45,9 +45,12 @@ 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) { +void downcast_test(A* a, A** ap) { B* b = a; // expected-warning{{incompatible pointer types initializing 'B *', expected 'A *'}} b = a; // expected-warning{{incompatible pointer types assigning 'B *', expected 'A *'}} + + B** bp = ap; // expected-warning{{incompatible pointer types initializing 'B **', expected 'A **'}} + bp = ap; // expected-warning{{incompatible pointer types assigning 'B **', expected 'A **'}} } int& cv(A*); @@ -73,3 +76,15 @@ void qualid_test(A *a, B *b, C *c) { int& i2 = qualid(b); float& f1 = qualid(c); } + + +@class NSException; +typedef struct { + void (*throw_exc)(id); +} +objc_exception_functions_t; + +void (*_NSExceptionRaiser(void))(NSException *) { + objc_exception_functions_t exc_funcs; + return exc_funcs.throw_exc; // expected-warning{{incompatible pointer types returning 'void (*)(NSException *)', expected 'void (*)(id)'}} +}