From 31862ba5ea70b1f2c81d03f8a0100b61cd6f06f6 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 1 Jul 2011 22:22:50 +0000 Subject: [PATCH] [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(&x); // Casting as (__strong NSString**). This currently only works for C++ named casts, C casts to follow. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@134273 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 4 + include/clang/Sema/Sema.h | 3 +- lib/AST/ASTContext.cpp | 19 +++ lib/Parse/ParseExprCXX.cpp | 13 +- lib/Sema/SemaCXXCast.cpp | 20 ++-- lib/Sema/SemaType.cpp | 111 ++++++++++++++++++ .../dcl.spec/dcl.type/dcl.spec.auto/p5.cpp | 2 +- test/SemaObjCXX/arc-type-conversion.mm | 24 +++- 8 files changed, 182 insertions(+), 14 deletions(-) diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index a761ba3449..cb9147a34c 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -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). /// diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 118130f8d9..797bfbaa29 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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, diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 4c42393365..aca011f9df 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -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()) + T = PT->getPointeeType(); + else if (const ReferenceType *RT = T->getAs()) + 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. diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 662a5abe4e..632499295f 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -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); diff --git a/lib/Sema/SemaCXXCast.cpp b/lib/Sema/SemaCXXCast.cpp index 2fca14f9d0..d053d5a9e9 100644 --- a/lib/Sema/SemaCXXCast.cpp +++ b/lib/Sema/SemaCXXCast.cpp @@ -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)); } diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 41d3916a50..5e809b0e35 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -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) { diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp index fabfb5329d..54da854dab 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp @@ -46,7 +46,7 @@ void j() { U v; // expected-error{{'auto' not allowed in template argument}} int n; - (void)dynamic_cast(S()); // expected-error{{'auto' not allowed here}} + (void)dynamic_cast(n); // expected-error{{'auto' not allowed here}} (void)static_cast(&n); // expected-error{{'auto' not allowed here}} (void)reinterpret_cast(&n); // expected-error{{'auto' not allowed here}} (void)const_cast(n); // expected-error{{'auto' not allowed here}} diff --git a/test/SemaObjCXX/arc-type-conversion.mm b/test/SemaObjCXX/arc-type-conversion.mm index b7da7034d1..7c6beaf1ad 100644 --- a/test/SemaObjCXX/arc-type-conversion.mm +++ b/test/SemaObjCXX/arc-type-conversion.mm @@ -42,8 +42,7 @@ void static_casts(id arg) { (void)static_cast(arg); // expected-error {{cannot cast from type 'id' to pointer type 'int *'}} (void)static_cast(arg); (void)static_cast<__autoreleasing id*>(arg); // expected-error{{cannot cast from type 'id' to pointer type '__autoreleasing id *'}} - (void)static_cast(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(arg); // expected-error {{cannot cast from type 'id' to pointer type '__strong id *'}} (void)static_cast<__autoreleasing id**>(voidp_val); (void)static_cast(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(static_cast<__strong id *>(vp)); + __weak NSString **wip2 = static_cast(static_cast<__weak id *>(vp)); + __autoreleasing id *aip2 = static_cast(static_cast<__autoreleasing id *>(vp)); + __unsafe_unretained id *uip2 = static_cast(static_cast<__unsafe_unretained id *>(vp)); + __strong id *sip3 = reinterpret_cast(reinterpret_cast<__strong id *>(vp)); + __weak id *wip3 = reinterpret_cast(reinterpret_cast<__weak id *>(vp)); + __autoreleasing id *aip3 = reinterpret_cast(reinterpret_cast<__autoreleasing id *>(vp)); + __unsafe_unretained id *uip3 = reinterpret_cast(reinterpret_cast<__unsafe_unretained id *>(vp)); + + Block_strong blk_strong1; + Block_strong blk_strong2 = static_cast(blk_strong1); + Block_autoreleasing *blk_auto = static_cast(pblk); +} -- 2.40.0