Improve our handling of cv-qualifiers in Objective-C pointer

conversions. Previously, we would end up collapsing qualification
conversions into the Objective-C pointer conversion step, including
(possibly) stripping qualifiers that shouldn't be removed.

This generalizes BuildSimilarlyQualifiedPointerType() to also work on
Objective-C object pointers, then eliminates the (redundant, not
totally correct) BuildSimilarlyQualifiedObjCObjectPointerType()
function.

Fixes <rdar://problem/8714395>.

llvm-svn: 120607
This commit is contained in:
Douglas Gregor 2010-12-01 21:43:58 +00:00
parent 3670ba5c87
commit 8d6d06761f
3 changed files with 62 additions and 43 deletions

View File

@ -1358,10 +1358,14 @@ bool Sema::IsComplexPromotion(QualType FromType, QualType ToType) {
/// if non-empty, will be a pointer to ToType that may or may not have
/// the right set of qualifiers on its pointee.
static QualType
BuildSimilarlyQualifiedPointerType(const PointerType *FromPtr,
BuildSimilarlyQualifiedPointerType(const Type *FromPtr,
QualType ToPointee, QualType ToType,
ASTContext &Context) {
QualType CanonFromPointee = Context.getCanonicalType(FromPtr->getPointeeType());
assert((FromPtr->getTypeClass() == Type::Pointer ||
FromPtr->getTypeClass() == Type::ObjCObjectPointer) &&
"Invalid similarly-qualified pointer type");
QualType CanonFromPointee
= Context.getCanonicalType(FromPtr->getPointeeType());
QualType CanonToPointee = Context.getCanonicalType(ToPointee);
Qualifiers Quals = CanonFromPointee.getQualifiers();
@ -1373,32 +1377,18 @@ BuildSimilarlyQualifiedPointerType(const PointerType *FromPtr,
// Build a pointer to ToPointee. It has the right qualifiers
// already.
if (isa<ObjCObjectPointerType>(ToType))
return Context.getObjCObjectPointerType(ToPointee);
return Context.getPointerType(ToPointee);
}
// Just build a canonical type that has the right qualifiers.
return Context.getPointerType(
Context.getQualifiedType(CanonToPointee.getLocalUnqualifiedType(),
Quals));
}
/// BuildSimilarlyQualifiedObjCObjectPointerType - In a pointer conversion from
/// the FromType, which is an objective-c pointer, to ToType, which may or may
/// not have the right set of qualifiers.
static QualType
BuildSimilarlyQualifiedObjCObjectPointerType(QualType FromType,
QualType ToType,
ASTContext &Context) {
QualType CanonFromType = Context.getCanonicalType(FromType);
QualType CanonToType = Context.getCanonicalType(ToType);
Qualifiers Quals = CanonFromType.getQualifiers();
// Exact qualifier match -> return the pointer type we're converting to.
if (CanonToType.getLocalQualifiers() == Quals)
return ToType;
QualType QualifiedCanonToPointee
= Context.getQualifiedType(CanonToPointee.getLocalUnqualifiedType(), Quals);
// Just build a canonical type that has the right qualifiers.
return Context.getQualifiedType(CanonToType.getLocalUnqualifiedType(), Quals);
if (isa<ObjCObjectPointerType>(ToType))
return Context.getObjCObjectPointerType(QualifiedCanonToPointee);
return Context.getPointerType(QualifiedCanonToPointee);
}
static bool isNullPointerConstantForConversion(Expr *Expr,
@ -1482,10 +1472,11 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType,
// , including objective-c pointers.
QualType ToPointeeType = ToTypePtr->getPointeeType();
if (FromType->isObjCObjectPointerType() && ToPointeeType->isVoidType()) {
ConvertedType = BuildSimilarlyQualifiedObjCObjectPointerType(FromType,
ConvertedType = BuildSimilarlyQualifiedPointerType(
FromType->getAs<ObjCObjectPointerType>(),
ToPointeeType,
ToType, Context);
return true;
}
const PointerType *FromTypePtr = FromType->getAs<PointerType>();
if (!FromTypePtr)
@ -1561,6 +1552,12 @@ bool Sema::isObjCPointerConversion(QualType FromType, QualType ToType,
FromType->getAs<ObjCObjectPointerType>();
if (ToObjCPtr && FromObjCPtr) {
// If the pointee types are the same (ignoring qualifications),
// then this is not a pointer conversion.
if (Context.hasSameUnqualifiedType(ToObjCPtr->getPointeeType(),
FromObjCPtr->getPointeeType()))
return false;
// Objective C++: We're able to convert between "id" or "Class" and a
// pointer to any interface (in both directions).
if (ToObjCPtr->isObjCBuiltinType() && FromObjCPtr->isObjCBuiltinType()) {
@ -1584,7 +1581,9 @@ bool Sema::isObjCPointerConversion(QualType FromType, QualType ToType,
!ToObjCPtr->getPointeeType().isAtLeastAsQualifiedAs(
FromObjCPtr->getPointeeType()))
return false;
ConvertedType = ToType;
ConvertedType = BuildSimilarlyQualifiedPointerType(FromObjCPtr,
ToObjCPtr->getPointeeType(),
ToType, Context);
return true;
}
@ -1593,7 +1592,9 @@ bool Sema::isObjCPointerConversion(QualType FromType, QualType ToType,
// interfaces, which is permitted. However, we're going to
// complain about it.
IncompatibleObjC = true;
ConvertedType = FromType;
ConvertedType = BuildSimilarlyQualifiedPointerType(FromObjCPtr,
ToObjCPtr->getPointeeType(),
ToType, Context);
return true;
}
}
@ -1636,7 +1637,7 @@ bool Sema::isObjCPointerConversion(QualType FromType, QualType ToType,
IncompatibleObjC)) {
// We always complain about this conversion.
IncompatibleObjC = true;
ConvertedType = ToType;
ConvertedType = Context.getPointerType(ConvertedType);
return true;
}
// Allow conversion of pointee being objective-c pointer to another one;
@ -1645,7 +1646,7 @@ bool Sema::isObjCPointerConversion(QualType FromType, QualType ToType,
ToPointeeType->getAs<ObjCObjectPointerType>() &&
isObjCPointerConversion(FromPointeeType, ToPointeeType, ConvertedType,
IncompatibleObjC)) {
ConvertedType = ToType;
ConvertedType = Context.getPointerType(ConvertedType);
return true;
}

View File

@ -39,8 +39,8 @@ void foo(const I *p, I* sel) {
@interface DerivedFromI : I
@end
void accept_derived(DerivedFromI*); // expected-note{{candidate function not viable: cannot convert from superclass 'I *' to subclass 'DerivedFromI *' for 1st argument}}
void accept_derived(DerivedFromI*);
void test_base_to_derived(I* i) {
accept_derived(i); // expected-error{{no matching function for call to 'accept_derived'}}
accept_derived(i); // expected-warning{{incompatible pointer types converting 'I *' to type 'DerivedFromI *'}}
}

View File

@ -31,8 +31,8 @@ int& f(A*); // expected-note {{candidate}}
float& f(B*); // expected-note {{candidate}}
void g(A*);
int& h(A*);
float& h(id);
int& h(A*); // expected-note{{candidate}}
float& h(id); // expected-note{{candidate}}
void test0(A* a, B* b, id val) {
int& i1 = f(a);
@ -46,13 +46,14 @@ void test0(A* a, B* b, id val) {
g(val);
int& i2 = h(a);
float& f3 = h(val);
// int& i3 = h(b); FIXME: we match GCC here, but shouldn't this work?
// FIXME: we match GCC here, but shouldn't this work?
int& i3 = h(b); // expected-error{{call to 'h' is ambiguous}}
}
// We make these errors instead of warnings. Is that okay?
void test1(A* a) {
B* b = a; // expected-error{{cannot initialize a variable of type 'B *' with an lvalue of type 'A *'}}
B *c; c = a; // expected-error{{assigning to 'B *' from incompatible type 'A *'}}
B* b = a; // expected-warning{{incompatible pointer types converting 'A *' to type 'B *'}}
B *c; c = a; // expected-warning{{incompatible pointer types assigning to 'A *' from 'B *'}}
}
void test2(A** ap) {
@ -64,18 +65,16 @@ void test2(A** ap) {
int& cv(A*); // expected-note {{previous declaration}} expected-note 2 {{not viable}}
float& cv(const A*); // expected-error {{cannot be overloaded}}
int& cv2(void*); // expected-note 2 {{candidate}}
float& cv2(const void*); // expected-note 2 {{candidate}}
int& cv2(void*);
float& cv2(const void*);
void cv_test(A* a, B* b, const A* ac, const B* bc) {
int &i1 = cv(a);
int &i2 = cv(b);
float &f1 = cv(ac); // expected-error {{no matching function}}
float &f2 = cv(bc); // expected-error {{no matching function}}
// FIXME: these should not be ambiguous
int& i3 = cv2(a); // expected-error {{ambiguous}}
float& f3 = cv2(ac); // expected-error {{ambiguous}}
int& i3 = cv2(a);
float& f3 = cv2(ac);
}
// We agree with GCC that these can't be overloaded.
@ -124,3 +123,22 @@ namespace test6 {
foo(b); // expected-error {{call to 'foo' is ambiguous}}
}
}
namespace rdar8714395 {
int &f(const void*);
float &f(const Foo*);
int &f2(const void*);
float &f2(Foo const* const *);
int &f3(const void*);
float &f3(Foo const**);
void g(Foo *p) {
float &fr = f(p);
float &fr2 = f2(&p);
int &ir = f3(&p);
}
}