[ARC] When casting from a pointer to an objective-c object with known ownership, if the

cast type has no ownership specified, implicitly "transfer" the ownership of the cast'ed type
to the cast type:

id x;
static_cast<NSString**>(&x); // Casting as (__strong NSString**).

This currently only works for C++ named casts, C casts to follow.

llvm-svn: 134273
This commit is contained in:
Argyrios Kyrtzidis 2011-07-01 22:22:50 +00:00
parent e9b5c85041
commit 7451d1cd00
8 changed files with 182 additions and 14 deletions

View File

@ -1347,6 +1347,10 @@ public:
/// integer type.
QualType getPromotedIntegerType(QualType PromotableType) const;
/// \brief Recurses in pointer/array types until it finds an objc retainable
/// type and returns its ownership.
Qualifiers::ObjCLifetime getInnerObjCOwnership(QualType T) const;
/// \brief Whether this is a promotable bitfield reference according
/// to C99 6.3.1.1p2, bullet 2 (and GCC extensions).
///

View File

@ -783,6 +783,7 @@ public:
QualType BuildParenType(QualType T);
TypeSourceInfo *GetTypeForDeclarator(Declarator &D, Scope *S);
TypeSourceInfo *GetTypeForDeclaratorCast(Declarator &D, QualType FromTy);
TypeSourceInfo *GetTypeSourceInfoForDeclarator(Declarator &D, QualType T,
TypeSourceInfo *ReturnTypeInfo);
/// \brief Package the given type and TSI into a ParsedType.
@ -2825,7 +2826,7 @@ public:
ExprResult ActOnCXXNamedCast(SourceLocation OpLoc,
tok::TokenKind Kind,
SourceLocation LAngleBracketLoc,
ParsedType Ty,
Declarator &D,
SourceLocation RAngleBracketLoc,
SourceLocation LParenLoc,
Expr *E,

View File

@ -3512,6 +3512,25 @@ QualType ASTContext::getPromotedIntegerType(QualType Promotable) const {
return (PromotableSize != IntSize) ? IntTy : UnsignedIntTy;
}
/// \brief Recurses in pointer/array types until it finds an objc retainable
/// type and returns its ownership.
Qualifiers::ObjCLifetime ASTContext::getInnerObjCOwnership(QualType T) const {
while (!T.isNull()) {
if (T.getObjCLifetime() != Qualifiers::OCL_None)
return T.getObjCLifetime();
if (T->isArrayType())
T = getBaseElementType(T);
else if (const PointerType *PT = T->getAs<PointerType>())
T = PT->getPointeeType();
else if (const ReferenceType *RT = T->getAs<ReferenceType>())
RT->getPointeeType();
else
break;
}
return Qualifiers::OCL_None;
}
/// getIntegerTypeOrder - Returns the highest ranked integer type:
/// C99 6.3.1.8p1. If LHS > RHS, return 1. If LHS == RHS, return 0. If
/// LHS < RHS, return -1.

View File

@ -538,7 +538,14 @@ ExprResult Parser::ParseCXXCasts() {
if (ExpectAndConsume(tok::less, diag::err_expected_less_after, CastName))
return ExprError();
TypeResult CastTy = ParseTypeName();
// Parse the common declaration-specifiers piece.
DeclSpec DS(AttrFactory);
ParseSpecifierQualifierList(DS);
// Parse the abstract-declarator, if present.
Declarator DeclaratorInfo(DS, Declarator::TypeNameContext);
ParseDeclarator(DeclaratorInfo);
SourceLocation RAngleBracketLoc = Tok.getLocation();
if (ExpectAndConsume(tok::greater, diag::err_expected_greater))
@ -554,9 +561,9 @@ ExprResult Parser::ParseCXXCasts() {
// Match the ')'.
RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc);
if (!Result.isInvalid() && !CastTy.isInvalid())
if (!Result.isInvalid() && !DeclaratorInfo.isInvalidType())
Result = Actions.ActOnCXXNamedCast(OpLoc, Kind,
LAngleBracketLoc, CastTy.get(),
LAngleBracketLoc, DeclaratorInfo,
RAngleBracketLoc,
LParenLoc, Result.take(), RParenLoc);

View File

@ -134,17 +134,23 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr,
/// ActOnCXXNamedCast - Parse {dynamic,static,reinterpret,const}_cast's.
ExprResult
Sema::ActOnCXXNamedCast(SourceLocation OpLoc, tok::TokenKind Kind,
SourceLocation LAngleBracketLoc, ParsedType Ty,
SourceLocation LAngleBracketLoc, Declarator &D,
SourceLocation RAngleBracketLoc,
SourceLocation LParenLoc, Expr *E,
SourceLocation RParenLoc) {
TypeSourceInfo *DestTInfo;
QualType DestType = GetTypeFromParser(Ty, &DestTInfo);
if (!DestTInfo)
DestTInfo = Context.getTrivialTypeSourceInfo(DestType, SourceLocation());
return BuildCXXNamedCast(OpLoc, Kind, DestTInfo, move(E),
assert(!D.isInvalidType());
TypeSourceInfo *TInfo = GetTypeForDeclaratorCast(D, E->getType());
if (D.isInvalidType())
return ExprError();
if (getLangOptions().CPlusPlus) {
// Check that there are no default arguments (C++ only).
CheckExtraCXXDefaultArguments(D);
}
return BuildCXXNamedCast(OpLoc, Kind, TInfo, move(E),
SourceRange(LAngleBracketLoc, RAngleBracketLoc),
SourceRange(LParenLoc, RParenLoc));
}

View File

@ -2534,6 +2534,117 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S) {
return GetFullTypeForDeclarator(state, T, ReturnTypeInfo);
}
static void transferARCOwnershipToDeclSpec(Sema &S,
QualType &declSpecTy,
Qualifiers::ObjCLifetime ownership) {
if (declSpecTy->isObjCRetainableType() &&
declSpecTy.getObjCLifetime() == Qualifiers::OCL_None) {
Qualifiers qs;
qs.addObjCLifetime(ownership);
declSpecTy = S.Context.getQualifiedType(declSpecTy, qs);
}
return;
}
static void transferARCOwnershipToDeclaratorChunk(TypeProcessingState &state,
Qualifiers::ObjCLifetime ownership,
unsigned chunkIndex) {
Sema &S = state.getSema();
Declarator &D = state.getDeclarator();
// Look for an explicit lifetime attribute.
DeclaratorChunk &chunk = D.getTypeObject(chunkIndex);
for (const AttributeList *attr = chunk.getAttrs(); attr;
attr = attr->getNext())
if (attr->getKind() == AttributeList::AT_objc_ownership)
return;
const char *attrStr = 0;
switch (ownership) {
case Qualifiers::OCL_None: llvm_unreachable("no ownership!"); break;
case Qualifiers::OCL_ExplicitNone: attrStr = "none"; break;
case Qualifiers::OCL_Strong: attrStr = "strong"; break;
case Qualifiers::OCL_Weak: attrStr = "weak"; break;
case Qualifiers::OCL_Autoreleasing: attrStr = "autoreleasing"; break;
}
if (!attrStr)
return;
// If there wasn't one, add one (with an invalid source location
// so that we don't make an AttributedType for it).
AttributeList *attr = D.getAttributePool()
.create(&S.Context.Idents.get("objc_ownership"), SourceLocation(),
/*scope*/ 0, SourceLocation(),
&S.Context.Idents.get(attrStr), SourceLocation(),
/*args*/ 0, 0,
/*declspec*/ false, /*C++0x*/ false);
spliceAttrIntoList(*attr, chunk.getAttrListRef());
// TODO: mark whether we did this inference?
}
static void transferARCOwnership(TypeProcessingState &state,
QualType &declSpecTy,
Qualifiers::ObjCLifetime ownership) {
Sema &S = state.getSema();
Declarator &D = state.getDeclarator();
int inner = -1;
for (unsigned i = 0, e = D.getNumTypeObjects(); i != e; ++i) {
DeclaratorChunk &chunk = D.getTypeObject(i);
switch (chunk.Kind) {
case DeclaratorChunk::Paren:
// Ignore parens.
break;
case DeclaratorChunk::Array:
case DeclaratorChunk::Reference:
case DeclaratorChunk::Pointer:
inner = i;
break;
case DeclaratorChunk::BlockPointer:
return transferARCOwnershipToDeclaratorChunk(state, ownership, i);
case DeclaratorChunk::Function:
case DeclaratorChunk::MemberPointer:
return;
}
}
if (inner == -1)
return transferARCOwnershipToDeclSpec(S, declSpecTy, ownership);
DeclaratorChunk &chunk = D.getTypeObject(inner);
if (chunk.Kind == DeclaratorChunk::Pointer) {
if (declSpecTy->isObjCRetainableType())
return transferARCOwnershipToDeclSpec(S, declSpecTy, ownership);
if (declSpecTy->isObjCObjectType())
return transferARCOwnershipToDeclaratorChunk(state, ownership, inner);
} else {
assert(chunk.Kind == DeclaratorChunk::Array ||
chunk.Kind == DeclaratorChunk::Reference);
return transferARCOwnershipToDeclSpec(S, declSpecTy, ownership);
}
}
TypeSourceInfo *Sema::GetTypeForDeclaratorCast(Declarator &D, QualType FromTy) {
TypeProcessingState state(*this, D);
TypeSourceInfo *ReturnTypeInfo = 0;
QualType declSpecTy = GetDeclSpecTypeForDeclarator(state, ReturnTypeInfo);
if (declSpecTy.isNull())
return Context.getNullTypeSourceInfo();
if (getLangOptions().ObjCAutoRefCount) {
Qualifiers::ObjCLifetime ownership = Context.getInnerObjCOwnership(FromTy);
if (ownership != Qualifiers::OCL_None)
transferARCOwnership(state, declSpecTy, ownership);
}
return GetFullTypeForDeclarator(state, declSpecTy, ReturnTypeInfo);
}
/// Map an AttributedType::Kind to an AttributeList::Kind.
static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) {
switch (kind) {

View File

@ -46,7 +46,7 @@ void j() {
U<auto> v; // expected-error{{'auto' not allowed in template argument}}
int n;
(void)dynamic_cast<auto&>(S()); // expected-error{{'auto' not allowed here}}
(void)dynamic_cast<auto&>(n); // expected-error{{'auto' not allowed here}}
(void)static_cast<auto*>(&n); // expected-error{{'auto' not allowed here}}
(void)reinterpret_cast<auto*>(&n); // expected-error{{'auto' not allowed here}}
(void)const_cast<auto>(n); // expected-error{{'auto' not allowed here}}

View File

@ -42,8 +42,7 @@ void static_casts(id arg) {
(void)static_cast<int*>(arg); // expected-error {{cannot cast from type 'id' to pointer type 'int *'}}
(void)static_cast<id>(arg);
(void)static_cast<__autoreleasing id*>(arg); // expected-error{{cannot cast from type 'id' to pointer type '__autoreleasing id *'}}
(void)static_cast<id*>(arg); // expected-error {{cannot cast from type 'id' to pointer type '__autoreleasing id *'}} \
// expected-error{{pointer to non-const type 'id' with no explicit ownership}}
(void)static_cast<id*>(arg); // expected-error {{cannot cast from type 'id' to pointer type '__strong id *'}}
(void)static_cast<__autoreleasing id**>(voidp_val);
(void)static_cast<void*>(voidp_val);
@ -196,3 +195,24 @@ void from_void(void *vp) {
aip = vp; // expected-error{{assigning to '__autoreleasing id *' from incompatible type 'void *'}}
uip = vp; // expected-error{{assigning to '__unsafe_unretained id *' from incompatible type 'void *'}}
}
typedef void (^Block)();
typedef void (^Block_strong)() __strong;
typedef void (^Block_autoreleasing)() __autoreleasing;
@class NSString;
void ownership_transfer_in_cast(void *vp, Block *pblk) {
__strong NSString **sip2 = static_cast<NSString **>(static_cast<__strong id *>(vp));
__weak NSString **wip2 = static_cast<NSString **>(static_cast<__weak id *>(vp));
__autoreleasing id *aip2 = static_cast<id *>(static_cast<__autoreleasing id *>(vp));
__unsafe_unretained id *uip2 = static_cast<id *>(static_cast<__unsafe_unretained id *>(vp));
__strong id *sip3 = reinterpret_cast<id *>(reinterpret_cast<__strong id *>(vp));
__weak id *wip3 = reinterpret_cast<id *>(reinterpret_cast<__weak id *>(vp));
__autoreleasing id *aip3 = reinterpret_cast<id *>(reinterpret_cast<__autoreleasing id *>(vp));
__unsafe_unretained id *uip3 = reinterpret_cast<id *>(reinterpret_cast<__unsafe_unretained id *>(vp));
Block_strong blk_strong1;
Block_strong blk_strong2 = static_cast<Block>(blk_strong1);
Block_autoreleasing *blk_auto = static_cast<Block*>(pblk);
}