1249423Sdim//===--- TransBlockObjCVariable.cpp - Transformations to ARC mode ---------===//
2224135Sdim//
3224135Sdim//                     The LLVM Compiler Infrastructure
4224135Sdim//
5224135Sdim// This file is distributed under the University of Illinois Open Source
6224135Sdim// License. See LICENSE.TXT for details.
7224135Sdim//
8224135Sdim//===----------------------------------------------------------------------===//
9224135Sdim//
10224135Sdim// rewriteBlockObjCVariable:
11224135Sdim//
12239462Sdim// Adding __block to an obj-c variable could be either because the variable
13224135Sdim// is used for output storage or the user wanted to break a retain cycle.
14224135Sdim// This transformation checks whether a reference of the variable for the block
15224135Sdim// is actually needed (it is assigned to or its address is taken) or not.
16224135Sdim// If the reference is not needed it will assume __block was added to break a
17224135Sdim// cycle so it will remove '__block' and add __weak/__unsafe_unretained.
18224135Sdim// e.g
19224135Sdim//
20224135Sdim//   __block Foo *x;
21224135Sdim//   bar(^ { [x cake]; });
22224135Sdim// ---->
23224135Sdim//   __weak Foo *x;
24224135Sdim//   bar(^ { [x cake]; });
25224135Sdim//
26224135Sdim//===----------------------------------------------------------------------===//
27224135Sdim
28224135Sdim#include "Transforms.h"
29224135Sdim#include "Internals.h"
30239462Sdim#include "clang/AST/ASTContext.h"
31249423Sdim#include "clang/AST/Attr.h"
32224135Sdim#include "clang/Basic/SourceManager.h"
33224135Sdim
34224135Sdimusing namespace clang;
35224135Sdimusing namespace arcmt;
36224135Sdimusing namespace trans;
37224135Sdim
38224135Sdimnamespace {
39224135Sdim
40224135Sdimclass RootBlockObjCVarRewriter :
41224135Sdim                          public RecursiveASTVisitor<RootBlockObjCVarRewriter> {
42234353Sdim  llvm::DenseSet<VarDecl *> &VarsToChange;
43224135Sdim
44224135Sdim  class BlockVarChecker : public RecursiveASTVisitor<BlockVarChecker> {
45224135Sdim    VarDecl *Var;
46224135Sdim
47224135Sdim    typedef RecursiveASTVisitor<BlockVarChecker> base;
48224135Sdim  public:
49224135Sdim    BlockVarChecker(VarDecl *var) : Var(var) { }
50224135Sdim
51224135Sdim    bool TraverseImplicitCastExpr(ImplicitCastExpr *castE) {
52234353Sdim      if (DeclRefExpr *
53234353Sdim            ref = dyn_cast<DeclRefExpr>(castE->getSubExpr())) {
54224135Sdim        if (ref->getDecl() == Var) {
55224135Sdim          if (castE->getCastKind() == CK_LValueToRValue)
56224135Sdim            return true; // Using the value of the variable.
57224135Sdim          if (castE->getCastKind() == CK_NoOp && castE->isLValue() &&
58234353Sdim              Var->getASTContext().getLangOpts().CPlusPlus)
59224135Sdim            return true; // Binding to const C++ reference.
60224135Sdim        }
61224135Sdim      }
62224135Sdim
63224135Sdim      return base::TraverseImplicitCastExpr(castE);
64224135Sdim    }
65224135Sdim
66234353Sdim    bool VisitDeclRefExpr(DeclRefExpr *E) {
67224135Sdim      if (E->getDecl() == Var)
68224135Sdim        return false; // The reference of the variable, and not just its value,
69224135Sdim                      //  is needed.
70224135Sdim      return true;
71224135Sdim    }
72224135Sdim  };
73224135Sdim
74224135Sdimpublic:
75239462Sdim  RootBlockObjCVarRewriter(llvm::DenseSet<VarDecl *> &VarsToChange)
76239462Sdim    : VarsToChange(VarsToChange) { }
77224135Sdim
78224135Sdim  bool VisitBlockDecl(BlockDecl *block) {
79226633Sdim    SmallVector<VarDecl *, 4> BlockVars;
80224135Sdim
81224135Sdim    for (BlockDecl::capture_iterator
82224135Sdim           I = block->capture_begin(), E = block->capture_end(); I != E; ++I) {
83224135Sdim      VarDecl *var = I->getVariable();
84224135Sdim      if (I->isByRef() &&
85224135Sdim          var->getType()->isObjCObjectPointerType() &&
86224135Sdim          isImplicitStrong(var->getType())) {
87224135Sdim        BlockVars.push_back(var);
88224135Sdim      }
89224135Sdim    }
90224135Sdim
91224135Sdim    for (unsigned i = 0, e = BlockVars.size(); i != e; ++i) {
92224135Sdim      VarDecl *var = BlockVars[i];
93224135Sdim
94224135Sdim      BlockVarChecker checker(var);
95224135Sdim      bool onlyValueOfVarIsNeeded = checker.TraverseStmt(block->getBody());
96234353Sdim      if (onlyValueOfVarIsNeeded)
97234353Sdim        VarsToChange.insert(var);
98234353Sdim      else
99234353Sdim        VarsToChange.erase(var);
100224135Sdim    }
101224135Sdim
102224135Sdim    return true;
103224135Sdim  }
104224135Sdim
105224135Sdimprivate:
106224135Sdim  bool isImplicitStrong(QualType ty) {
107224135Sdim    if (isa<AttributedType>(ty.getTypePtr()))
108224135Sdim      return false;
109224135Sdim    return ty.getLocalQualifiers().getObjCLifetime() == Qualifiers::OCL_Strong;
110224135Sdim  }
111224135Sdim};
112224135Sdim
113224135Sdimclass BlockObjCVarRewriter : public RecursiveASTVisitor<BlockObjCVarRewriter> {
114234353Sdim  llvm::DenseSet<VarDecl *> &VarsToChange;
115224135Sdim
116224135Sdimpublic:
117239462Sdim  BlockObjCVarRewriter(llvm::DenseSet<VarDecl *> &VarsToChange)
118239462Sdim    : VarsToChange(VarsToChange) { }
119224135Sdim
120224135Sdim  bool TraverseBlockDecl(BlockDecl *block) {
121239462Sdim    RootBlockObjCVarRewriter(VarsToChange).TraverseDecl(block);
122224135Sdim    return true;
123224135Sdim  }
124224135Sdim};
125224135Sdim
126224135Sdim} // anonymous namespace
127224135Sdim
128234353Sdimvoid BlockObjCVariableTraverser::traverseBody(BodyContext &BodyCtx) {
129234353Sdim  MigrationPass &Pass = BodyCtx.getMigrationContext().Pass;
130234353Sdim  llvm::DenseSet<VarDecl *> VarsToChange;
131234353Sdim
132239462Sdim  BlockObjCVarRewriter trans(VarsToChange);
133234353Sdim  trans.TraverseStmt(BodyCtx.getTopStmt());
134234353Sdim
135234353Sdim  for (llvm::DenseSet<VarDecl *>::iterator
136234353Sdim         I = VarsToChange.begin(), E = VarsToChange.end(); I != E; ++I) {
137234353Sdim    VarDecl *var = *I;
138234353Sdim    BlocksAttr *attr = var->getAttr<BlocksAttr>();
139234353Sdim    if(!attr)
140234353Sdim      continue;
141234353Sdim    bool useWeak = canApplyWeak(Pass.Ctx, var->getType());
142234353Sdim    SourceManager &SM = Pass.Ctx.getSourceManager();
143234353Sdim    Transaction Trans(Pass.TA);
144234353Sdim    Pass.TA.replaceText(SM.getExpansionLoc(attr->getLocation()),
145234353Sdim                        "__block",
146234353Sdim                        useWeak ? "__weak" : "__unsafe_unretained");
147234353Sdim  }
148224135Sdim}
149