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