blob: ba556277cc814b87144bd4ef7d26624539a5ec42 [file] [log] [blame]
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +00001//===--- TransBlockObjCVariable.cpp - Tranformations to ARC mode ----------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// rewriteBlockObjCVariable:
11//
12// Adding __block to an obj-c variable could be either because the the variable
13// is used for output storage or the user wanted to break a retain cycle.
14// This transformation checks whether a reference of the variable for the block
15// is actually needed (it is assigned to or its address is taken) or not.
16// If the reference is not needed it will assume __block was added to break a
17// cycle so it will remove '__block' and add __weak/__unsafe_unretained.
18// e.g
19//
20// __block Foo *x;
21// bar(^ { [x cake]; });
22// ---->
23// __weak Foo *x;
24// bar(^ { [x cake]; });
25//
26//===----------------------------------------------------------------------===//
27
28#include "Transforms.h"
29#include "Internals.h"
30#include "clang/Basic/SourceManager.h"
31
32using namespace clang;
33using namespace arcmt;
34using namespace trans;
35using llvm::StringRef;
36
37namespace {
38
39class RootBlockObjCVarRewriter :
40 public RecursiveASTVisitor<RootBlockObjCVarRewriter> {
41 MigrationPass &Pass;
42 llvm::DenseSet<VarDecl *> CheckedVars;
43
44 class BlockVarChecker : public RecursiveASTVisitor<BlockVarChecker> {
45 VarDecl *Var;
46
47 typedef RecursiveASTVisitor<BlockVarChecker> base;
48 public:
49 BlockVarChecker(VarDecl *var) : Var(var) { }
50
51 bool TraverseImplicitCastExpr(ImplicitCastExpr *castE) {
52 if (BlockDeclRefExpr *
53 ref = dyn_cast<BlockDeclRefExpr>(castE->getSubExpr())) {
54 if (ref->getDecl() == Var) {
55 if (castE->getCastKind() == CK_LValueToRValue)
56 return true; // Using the value of the variable.
57 if (castE->getCastKind() == CK_NoOp && castE->isLValue() &&
58 Var->getASTContext().getLangOptions().CPlusPlus)
59 return true; // Binding to const C++ reference.
60 }
61 }
62
63 return base::TraverseImplicitCastExpr(castE);
64 }
65
66 bool VisitBlockDeclRefExpr(BlockDeclRefExpr *E) {
67 if (E->getDecl() == Var)
68 return false; // The reference of the variable, and not just its value,
69 // is needed.
70 return true;
71 }
72 };
73
74public:
75 RootBlockObjCVarRewriter(MigrationPass &pass) : Pass(pass) { }
76
77 bool VisitBlockDecl(BlockDecl *block) {
78 llvm::SmallVector<VarDecl *, 4> BlockVars;
79
80 for (BlockDecl::capture_iterator
81 I = block->capture_begin(), E = block->capture_end(); I != E; ++I) {
82 VarDecl *var = I->getVariable();
83 if (I->isByRef() &&
84 !isAlreadyChecked(var) &&
85 var->getType()->isObjCObjectPointerType() &&
86 isImplicitStrong(var->getType())) {
87 BlockVars.push_back(var);
88 }
89 }
90
91 for (unsigned i = 0, e = BlockVars.size(); i != e; ++i) {
92 VarDecl *var = BlockVars[i];
93 CheckedVars.insert(var);
94
95 BlockVarChecker checker(var);
96 bool onlyValueOfVarIsNeeded = checker.TraverseStmt(block->getBody());
97 if (onlyValueOfVarIsNeeded) {
98 BlocksAttr *attr = var->getAttr<BlocksAttr>();
99 if(!attr)
100 continue;
101 bool hasARCRuntime = !Pass.Ctx.getLangOptions().ObjCNoAutoRefCountRuntime;
102 SourceManager &SM = Pass.Ctx.getSourceManager();
103 Transaction Trans(Pass.TA);
104 Pass.TA.replaceText(SM.getInstantiationLoc(attr->getLocation()),
105 "__block",
106 hasARCRuntime ? "__weak" : "__unsafe_unretained");
107 }
108
109 }
110
111 return true;
112 }
113
114private:
115 bool isAlreadyChecked(VarDecl *VD) {
116 return CheckedVars.count(VD);
117 }
118
119 bool isImplicitStrong(QualType ty) {
120 if (isa<AttributedType>(ty.getTypePtr()))
121 return false;
122 return ty.getLocalQualifiers().getObjCLifetime() == Qualifiers::OCL_Strong;
123 }
124};
125
126class BlockObjCVarRewriter : public RecursiveASTVisitor<BlockObjCVarRewriter> {
127 MigrationPass &Pass;
128
129public:
130 BlockObjCVarRewriter(MigrationPass &pass) : Pass(pass) { }
131
132 bool TraverseBlockDecl(BlockDecl *block) {
133 RootBlockObjCVarRewriter(Pass).TraverseDecl(block);
134 return true;
135 }
136};
137
138} // anonymous namespace
139
140void trans::rewriteBlockObjCVariable(MigrationPass &pass) {
141 BlockObjCVarRewriter trans(pass);
142 trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
143}