xref: /freebsd-src/contrib/llvm-project/clang/lib/ARCMigrate/TransBlockObjCVariable.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1*0b57cec5SDimitry Andric //===--- TransBlockObjCVariable.cpp - Transformations to ARC mode ---------===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric // rewriteBlockObjCVariable:
10*0b57cec5SDimitry Andric //
11*0b57cec5SDimitry Andric // Adding __block to an obj-c variable could be either because the variable
12*0b57cec5SDimitry Andric // is used for output storage or the user wanted to break a retain cycle.
13*0b57cec5SDimitry Andric // This transformation checks whether a reference of the variable for the block
14*0b57cec5SDimitry Andric // is actually needed (it is assigned to or its address is taken) or not.
15*0b57cec5SDimitry Andric // If the reference is not needed it will assume __block was added to break a
16*0b57cec5SDimitry Andric // cycle so it will remove '__block' and add __weak/__unsafe_unretained.
17*0b57cec5SDimitry Andric // e.g
18*0b57cec5SDimitry Andric //
19*0b57cec5SDimitry Andric //   __block Foo *x;
20*0b57cec5SDimitry Andric //   bar(^ { [x cake]; });
21*0b57cec5SDimitry Andric // ---->
22*0b57cec5SDimitry Andric //   __weak Foo *x;
23*0b57cec5SDimitry Andric //   bar(^ { [x cake]; });
24*0b57cec5SDimitry Andric //
25*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
26*0b57cec5SDimitry Andric 
27*0b57cec5SDimitry Andric #include "Transforms.h"
28*0b57cec5SDimitry Andric #include "Internals.h"
29*0b57cec5SDimitry Andric #include "clang/AST/ASTContext.h"
30*0b57cec5SDimitry Andric #include "clang/AST/Attr.h"
31*0b57cec5SDimitry Andric #include "clang/Basic/SourceManager.h"
32*0b57cec5SDimitry Andric 
33*0b57cec5SDimitry Andric using namespace clang;
34*0b57cec5SDimitry Andric using namespace arcmt;
35*0b57cec5SDimitry Andric using namespace trans;
36*0b57cec5SDimitry Andric 
37*0b57cec5SDimitry Andric namespace {
38*0b57cec5SDimitry Andric 
39*0b57cec5SDimitry Andric class RootBlockObjCVarRewriter :
40*0b57cec5SDimitry Andric                           public RecursiveASTVisitor<RootBlockObjCVarRewriter> {
41*0b57cec5SDimitry Andric   llvm::DenseSet<VarDecl *> &VarsToChange;
42*0b57cec5SDimitry Andric 
43*0b57cec5SDimitry Andric   class BlockVarChecker : public RecursiveASTVisitor<BlockVarChecker> {
44*0b57cec5SDimitry Andric     VarDecl *Var;
45*0b57cec5SDimitry Andric 
46*0b57cec5SDimitry Andric     typedef RecursiveASTVisitor<BlockVarChecker> base;
47*0b57cec5SDimitry Andric   public:
BlockVarChecker(VarDecl * var)48*0b57cec5SDimitry Andric     BlockVarChecker(VarDecl *var) : Var(var) { }
49*0b57cec5SDimitry Andric 
TraverseImplicitCastExpr(ImplicitCastExpr * castE)50*0b57cec5SDimitry Andric     bool TraverseImplicitCastExpr(ImplicitCastExpr *castE) {
51*0b57cec5SDimitry Andric       if (DeclRefExpr *
52*0b57cec5SDimitry Andric             ref = dyn_cast<DeclRefExpr>(castE->getSubExpr())) {
53*0b57cec5SDimitry Andric         if (ref->getDecl() == Var) {
54*0b57cec5SDimitry Andric           if (castE->getCastKind() == CK_LValueToRValue)
55*0b57cec5SDimitry Andric             return true; // Using the value of the variable.
56*0b57cec5SDimitry Andric           if (castE->getCastKind() == CK_NoOp && castE->isLValue() &&
57*0b57cec5SDimitry Andric               Var->getASTContext().getLangOpts().CPlusPlus)
58*0b57cec5SDimitry Andric             return true; // Binding to const C++ reference.
59*0b57cec5SDimitry Andric         }
60*0b57cec5SDimitry Andric       }
61*0b57cec5SDimitry Andric 
62*0b57cec5SDimitry Andric       return base::TraverseImplicitCastExpr(castE);
63*0b57cec5SDimitry Andric     }
64*0b57cec5SDimitry Andric 
VisitDeclRefExpr(DeclRefExpr * E)65*0b57cec5SDimitry Andric     bool VisitDeclRefExpr(DeclRefExpr *E) {
66*0b57cec5SDimitry Andric       if (E->getDecl() == Var)
67*0b57cec5SDimitry Andric         return false; // The reference of the variable, and not just its value,
68*0b57cec5SDimitry Andric                       //  is needed.
69*0b57cec5SDimitry Andric       return true;
70*0b57cec5SDimitry Andric     }
71*0b57cec5SDimitry Andric   };
72*0b57cec5SDimitry Andric 
73*0b57cec5SDimitry Andric public:
RootBlockObjCVarRewriter(llvm::DenseSet<VarDecl * > & VarsToChange)74*0b57cec5SDimitry Andric   RootBlockObjCVarRewriter(llvm::DenseSet<VarDecl *> &VarsToChange)
75*0b57cec5SDimitry Andric     : VarsToChange(VarsToChange) { }
76*0b57cec5SDimitry Andric 
VisitBlockDecl(BlockDecl * block)77*0b57cec5SDimitry Andric   bool VisitBlockDecl(BlockDecl *block) {
78*0b57cec5SDimitry Andric     SmallVector<VarDecl *, 4> BlockVars;
79*0b57cec5SDimitry Andric 
80*0b57cec5SDimitry Andric     for (const auto &I : block->captures()) {
81*0b57cec5SDimitry Andric       VarDecl *var = I.getVariable();
82*0b57cec5SDimitry Andric       if (I.isByRef() &&
83*0b57cec5SDimitry Andric           var->getType()->isObjCObjectPointerType() &&
84*0b57cec5SDimitry Andric           isImplicitStrong(var->getType())) {
85*0b57cec5SDimitry Andric         BlockVars.push_back(var);
86*0b57cec5SDimitry Andric       }
87*0b57cec5SDimitry Andric     }
88*0b57cec5SDimitry Andric 
89*0b57cec5SDimitry Andric     for (unsigned i = 0, e = BlockVars.size(); i != e; ++i) {
90*0b57cec5SDimitry Andric       VarDecl *var = BlockVars[i];
91*0b57cec5SDimitry Andric 
92*0b57cec5SDimitry Andric       BlockVarChecker checker(var);
93*0b57cec5SDimitry Andric       bool onlyValueOfVarIsNeeded = checker.TraverseStmt(block->getBody());
94*0b57cec5SDimitry Andric       if (onlyValueOfVarIsNeeded)
95*0b57cec5SDimitry Andric         VarsToChange.insert(var);
96*0b57cec5SDimitry Andric       else
97*0b57cec5SDimitry Andric         VarsToChange.erase(var);
98*0b57cec5SDimitry Andric     }
99*0b57cec5SDimitry Andric 
100*0b57cec5SDimitry Andric     return true;
101*0b57cec5SDimitry Andric   }
102*0b57cec5SDimitry Andric 
103*0b57cec5SDimitry Andric private:
isImplicitStrong(QualType ty)104*0b57cec5SDimitry Andric   bool isImplicitStrong(QualType ty) {
105*0b57cec5SDimitry Andric     if (isa<AttributedType>(ty.getTypePtr()))
106*0b57cec5SDimitry Andric       return false;
107*0b57cec5SDimitry Andric     return ty.getLocalQualifiers().getObjCLifetime() == Qualifiers::OCL_Strong;
108*0b57cec5SDimitry Andric   }
109*0b57cec5SDimitry Andric };
110*0b57cec5SDimitry Andric 
111*0b57cec5SDimitry Andric class BlockObjCVarRewriter : public RecursiveASTVisitor<BlockObjCVarRewriter> {
112*0b57cec5SDimitry Andric   llvm::DenseSet<VarDecl *> &VarsToChange;
113*0b57cec5SDimitry Andric 
114*0b57cec5SDimitry Andric public:
BlockObjCVarRewriter(llvm::DenseSet<VarDecl * > & VarsToChange)115*0b57cec5SDimitry Andric   BlockObjCVarRewriter(llvm::DenseSet<VarDecl *> &VarsToChange)
116*0b57cec5SDimitry Andric     : VarsToChange(VarsToChange) { }
117*0b57cec5SDimitry Andric 
TraverseBlockDecl(BlockDecl * block)118*0b57cec5SDimitry Andric   bool TraverseBlockDecl(BlockDecl *block) {
119*0b57cec5SDimitry Andric     RootBlockObjCVarRewriter(VarsToChange).TraverseDecl(block);
120*0b57cec5SDimitry Andric     return true;
121*0b57cec5SDimitry Andric   }
122*0b57cec5SDimitry Andric };
123*0b57cec5SDimitry Andric 
124*0b57cec5SDimitry Andric } // anonymous namespace
125*0b57cec5SDimitry Andric 
traverseBody(BodyContext & BodyCtx)126*0b57cec5SDimitry Andric void BlockObjCVariableTraverser::traverseBody(BodyContext &BodyCtx) {
127*0b57cec5SDimitry Andric   MigrationPass &Pass = BodyCtx.getMigrationContext().Pass;
128*0b57cec5SDimitry Andric   llvm::DenseSet<VarDecl *> VarsToChange;
129*0b57cec5SDimitry Andric 
130*0b57cec5SDimitry Andric   BlockObjCVarRewriter trans(VarsToChange);
131*0b57cec5SDimitry Andric   trans.TraverseStmt(BodyCtx.getTopStmt());
132*0b57cec5SDimitry Andric 
133*0b57cec5SDimitry Andric   for (llvm::DenseSet<VarDecl *>::iterator
134*0b57cec5SDimitry Andric          I = VarsToChange.begin(), E = VarsToChange.end(); I != E; ++I) {
135*0b57cec5SDimitry Andric     VarDecl *var = *I;
136*0b57cec5SDimitry Andric     BlocksAttr *attr = var->getAttr<BlocksAttr>();
137*0b57cec5SDimitry Andric     if(!attr)
138*0b57cec5SDimitry Andric       continue;
139*0b57cec5SDimitry Andric     bool useWeak = canApplyWeak(Pass.Ctx, var->getType());
140*0b57cec5SDimitry Andric     SourceManager &SM = Pass.Ctx.getSourceManager();
141*0b57cec5SDimitry Andric     Transaction Trans(Pass.TA);
142*0b57cec5SDimitry Andric     Pass.TA.replaceText(SM.getExpansionLoc(attr->getLocation()),
143*0b57cec5SDimitry Andric                         "__block",
144*0b57cec5SDimitry Andric                         useWeak ? "__weak" : "__unsafe_unretained");
145*0b57cec5SDimitry Andric   }
146*0b57cec5SDimitry Andric }
147