1 //===--- TransGCAttrs.cpp - Transformations 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 #include "Transforms.h" 11 #include "Internals.h" 12 #include "clang/Lex/Lexer.h" 13 #include "clang/Basic/SourceManager.h" 14 #include "clang/Analysis/Support/SaveAndRestore.h" 15 #include "clang/Sema/SemaDiagnostic.h" 16 #include "llvm/ADT/TinyPtrVector.h" 17 18 using namespace clang; 19 using namespace arcmt; 20 using namespace trans; 21 22 namespace { 23 24 /// \brief Collects all the places where GC attributes __strong/__weak occur. 25 class GCAttrsCollector : public RecursiveASTVisitor<GCAttrsCollector> { 26 MigrationContext &MigrateCtx; 27 bool FullyMigratable; 28 std::vector<ObjCPropertyDecl *> &AllProps; 29 30 typedef RecursiveASTVisitor<GCAttrsCollector> base; 31 public: 32 GCAttrsCollector(MigrationContext &ctx, 33 std::vector<ObjCPropertyDecl *> &AllProps) 34 : MigrateCtx(ctx), FullyMigratable(false), 35 AllProps(AllProps) { } 36 37 bool shouldWalkTypesOfTypeLocs() const { return false; } 38 39 bool VisitAttributedTypeLoc(AttributedTypeLoc TL) { 40 handleAttr(TL); 41 return true; 42 } 43 44 bool TraverseDecl(Decl *D) { 45 if (!D || D->isImplicit()) 46 return true; 47 48 SaveAndRestore<bool> Save(FullyMigratable, isMigratable(D)); 49 50 if (ObjCPropertyDecl *PropD = dyn_cast<ObjCPropertyDecl>(D)) { 51 lookForAttribute(PropD, PropD->getTypeSourceInfo()); 52 AllProps.push_back(PropD); 53 } else if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(D)) { 54 lookForAttribute(DD, DD->getTypeSourceInfo()); 55 } 56 return base::TraverseDecl(D); 57 } 58 59 void lookForAttribute(Decl *D, TypeSourceInfo *TInfo) { 60 if (!TInfo) 61 return; 62 TypeLoc TL = TInfo->getTypeLoc(); 63 while (TL) { 64 if (const QualifiedTypeLoc *QL = dyn_cast<QualifiedTypeLoc>(&TL)) { 65 TL = QL->getUnqualifiedLoc(); 66 } else if (const AttributedTypeLoc * 67 Attr = dyn_cast<AttributedTypeLoc>(&TL)) { 68 if (handleAttr(*Attr, D)) 69 break; 70 TL = Attr->getModifiedLoc(); 71 } else if (const ArrayTypeLoc *Arr = dyn_cast<ArrayTypeLoc>(&TL)) { 72 TL = Arr->getElementLoc(); 73 } else if (const PointerTypeLoc *PT = dyn_cast<PointerTypeLoc>(&TL)) { 74 TL = PT->getPointeeLoc(); 75 } else if (const ReferenceTypeLoc *RT = dyn_cast<ReferenceTypeLoc>(&TL)) 76 TL = RT->getPointeeLoc(); 77 else 78 break; 79 } 80 } 81 82 bool handleAttr(AttributedTypeLoc TL, Decl *D = 0) { 83 if (TL.getAttrKind() != AttributedType::attr_objc_ownership) 84 return false; 85 86 SourceLocation Loc = TL.getAttrNameLoc(); 87 unsigned RawLoc = Loc.getRawEncoding(); 88 if (MigrateCtx.AttrSet.count(RawLoc)) 89 return true; 90 91 ASTContext &Ctx = MigrateCtx.Pass.Ctx; 92 SourceManager &SM = Ctx.getSourceManager(); 93 if (Loc.isMacroID()) 94 Loc = SM.getImmediateExpansionRange(Loc).first; 95 llvm::SmallString<32> Buf; 96 bool Invalid = false; 97 StringRef Spell = Lexer::getSpelling( 98 SM.getSpellingLoc(TL.getAttrEnumOperandLoc()), 99 Buf, SM, Ctx.getLangOptions(), &Invalid); 100 if (Invalid) 101 return false; 102 MigrationContext::GCAttrOccurrence::AttrKind Kind; 103 if (Spell == "strong") 104 Kind = MigrationContext::GCAttrOccurrence::Strong; 105 else if (Spell == "weak") 106 Kind = MigrationContext::GCAttrOccurrence::Weak; 107 else 108 return false; 109 110 MigrateCtx.AttrSet.insert(RawLoc); 111 MigrateCtx.GCAttrs.push_back(MigrationContext::GCAttrOccurrence()); 112 MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs.back(); 113 114 Attr.Kind = Kind; 115 Attr.Loc = Loc; 116 Attr.ModifiedType = TL.getModifiedLoc().getType(); 117 Attr.Dcl = D; 118 Attr.FullyMigratable = FullyMigratable; 119 return true; 120 } 121 122 bool isMigratable(Decl *D) { 123 if (isa<TranslationUnitDecl>(D)) 124 return false; 125 126 if (isInMainFile(D)) 127 return true; 128 129 if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) 130 return FD->hasBody(); 131 132 if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D)) 133 return hasObjCImpl(ContD); 134 135 if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) { 136 for (CXXRecordDecl::method_iterator 137 MI = RD->method_begin(), ME = RD->method_end(); MI != ME; ++MI) { 138 if ((*MI)->isOutOfLine()) 139 return true; 140 } 141 return false; 142 } 143 144 return isMigratable(cast<Decl>(D->getDeclContext())); 145 } 146 147 static bool hasObjCImpl(Decl *D) { 148 if (!D) 149 return false; 150 if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D)) { 151 if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(ContD)) 152 return ID->getImplementation() != 0; 153 if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(ContD)) 154 return CD->getImplementation() != 0; 155 if (isa<ObjCImplDecl>(ContD)) 156 return true; 157 return false; 158 } 159 return false; 160 } 161 162 bool isInMainFile(Decl *D) { 163 if (!D) 164 return false; 165 166 for (Decl::redecl_iterator 167 I = D->redecls_begin(), E = D->redecls_end(); I != E; ++I) 168 if (!isInMainFile((*I)->getLocation())) 169 return false; 170 171 return true; 172 } 173 174 bool isInMainFile(SourceLocation Loc) { 175 if (Loc.isInvalid()) 176 return false; 177 178 SourceManager &SM = MigrateCtx.Pass.Ctx.getSourceManager(); 179 return SM.isInFileID(SM.getExpansionLoc(Loc), SM.getMainFileID()); 180 } 181 }; 182 183 } // anonymous namespace 184 185 static void clearRedundantStrongs(MigrationContext &MigrateCtx) { 186 TransformActions &TA = MigrateCtx.Pass.TA; 187 188 for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) { 189 MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i]; 190 if (Attr.Kind == MigrationContext::GCAttrOccurrence::Strong && 191 Attr.FullyMigratable && Attr.Dcl) { 192 TypeSourceInfo *TInfo = 0; 193 if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(Attr.Dcl)) 194 TInfo = DD->getTypeSourceInfo(); 195 else if (ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(Attr.Dcl)) 196 TInfo = PD->getTypeSourceInfo(); 197 if (!TInfo) 198 continue; 199 200 if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Strong) { 201 Transaction Trans(TA); 202 TA.remove(Attr.Loc); 203 MigrateCtx.RemovedAttrSet.insert(Attr.Loc.getRawEncoding()); 204 } 205 } 206 } 207 } 208 209 static void errorForGCAttrsOnNonObjC(MigrationContext &MigrateCtx) { 210 TransformActions &TA = MigrateCtx.Pass.TA; 211 212 for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) { 213 MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i]; 214 if (Attr.FullyMigratable && Attr.Dcl) { 215 if (Attr.ModifiedType.isNull()) 216 continue; 217 if (!Attr.ModifiedType->isObjCRetainableType()) { 218 TA.reportError("GC managed memory will become unmanaged in ARC", 219 Attr.Loc); 220 } 221 } 222 } 223 } 224 225 static void checkWeakGCAttrs(MigrationContext &MigrateCtx) { 226 TransformActions &TA = MigrateCtx.Pass.TA; 227 228 for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) { 229 MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i]; 230 if (Attr.Kind == MigrationContext::GCAttrOccurrence::Weak) { 231 if (Attr.ModifiedType.isNull() || 232 !Attr.ModifiedType->isObjCRetainableType()) 233 continue; 234 if (!canApplyWeak(MigrateCtx.Pass.Ctx, Attr.ModifiedType, 235 /*AllowOnUnknownClass=*/true)) { 236 Transaction Trans(TA); 237 if (!MigrateCtx.RemovedAttrSet.count(Attr.Loc.getRawEncoding())) 238 TA.replaceText(Attr.Loc, "__weak", "__unsafe_unretained"); 239 TA.clearDiagnostic(diag::err_arc_weak_no_runtime, 240 diag::err_arc_unsupported_weak_class, 241 Attr.Loc); 242 } 243 } 244 } 245 } 246 247 typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy; 248 249 static void checkAllAtProps(MigrationContext &MigrateCtx, 250 SourceLocation AtLoc, 251 IndivPropsTy &IndProps) { 252 if (IndProps.empty()) 253 return; 254 255 for (IndivPropsTy::iterator 256 PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) { 257 QualType T = (*PI)->getType(); 258 if (T.isNull() || !T->isObjCRetainableType()) 259 return; 260 } 261 262 SmallVector<std::pair<AttributedTypeLoc, ObjCPropertyDecl *>, 4> ATLs; 263 bool hasWeak = false, hasStrong = false; 264 for (IndivPropsTy::iterator 265 PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) { 266 ObjCPropertyDecl *PD = *PI; 267 TypeSourceInfo *TInfo = PD->getTypeSourceInfo(); 268 if (!TInfo) 269 return; 270 TypeLoc TL = TInfo->getTypeLoc(); 271 if (AttributedTypeLoc *ATL = dyn_cast<AttributedTypeLoc>(&TL)) { 272 ATLs.push_back(std::make_pair(*ATL, PD)); 273 if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Weak) { 274 hasWeak = true; 275 } else if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Strong) 276 hasStrong = true; 277 else 278 return; 279 } 280 } 281 if (ATLs.empty()) 282 return; 283 if (hasWeak && hasStrong) 284 return; 285 286 TransformActions &TA = MigrateCtx.Pass.TA; 287 Transaction Trans(TA); 288 289 if (GCAttrsCollector::hasObjCImpl( 290 cast<Decl>(IndProps.front()->getDeclContext()))) { 291 if (hasWeak) 292 MigrateCtx.AtPropsWeak.insert(AtLoc.getRawEncoding()); 293 294 } else { 295 StringRef toAttr = "strong"; 296 if (hasWeak) { 297 if (canApplyWeak(MigrateCtx.Pass.Ctx, IndProps.front()->getType(), 298 /*AllowOnUnkwownClass=*/true)) 299 toAttr = "weak"; 300 else 301 toAttr = "unsafe_unretained"; 302 } 303 if (!MigrateCtx.rewritePropertyAttribute("assign", toAttr, AtLoc)) { 304 return; 305 } 306 } 307 308 for (unsigned i = 0, e = ATLs.size(); i != e; ++i) { 309 SourceLocation Loc = ATLs[i].first.getAttrNameLoc(); 310 if (Loc.isMacroID()) 311 Loc = MigrateCtx.Pass.Ctx.getSourceManager() 312 .getImmediateExpansionRange(Loc).first; 313 TA.remove(Loc); 314 TA.clearDiagnostic(diag::err_objc_property_attr_mutually_exclusive, AtLoc); 315 TA.clearDiagnostic(diag::err_arc_inconsistent_property_ownership, 316 ATLs[i].second->getLocation()); 317 MigrateCtx.RemovedAttrSet.insert(Loc.getRawEncoding()); 318 } 319 } 320 321 static void checkAllProps(MigrationContext &MigrateCtx, 322 std::vector<ObjCPropertyDecl *> &AllProps) { 323 typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy; 324 llvm::DenseMap<unsigned, IndivPropsTy> AtProps; 325 326 for (unsigned i = 0, e = AllProps.size(); i != e; ++i) { 327 ObjCPropertyDecl *PD = AllProps[i]; 328 if (PD->getPropertyAttributesAsWritten() & 329 ObjCPropertyDecl::OBJC_PR_assign) { 330 SourceLocation AtLoc = PD->getAtLoc(); 331 if (AtLoc.isInvalid()) 332 continue; 333 unsigned RawAt = AtLoc.getRawEncoding(); 334 AtProps[RawAt].push_back(PD); 335 } 336 } 337 338 for (llvm::DenseMap<unsigned, IndivPropsTy>::iterator 339 I = AtProps.begin(), E = AtProps.end(); I != E; ++I) { 340 SourceLocation AtLoc = SourceLocation::getFromRawEncoding(I->first); 341 IndivPropsTy &IndProps = I->second; 342 checkAllAtProps(MigrateCtx, AtLoc, IndProps); 343 } 344 } 345 346 void GCAttrsTraverser::traverseTU(MigrationContext &MigrateCtx) { 347 std::vector<ObjCPropertyDecl *> AllProps; 348 GCAttrsCollector(MigrateCtx, AllProps).TraverseDecl( 349 MigrateCtx.Pass.Ctx.getTranslationUnitDecl()); 350 351 clearRedundantStrongs(MigrateCtx); 352 errorForGCAttrsOnNonObjC(MigrateCtx); 353 checkAllProps(MigrateCtx, AllProps); 354 checkWeakGCAttrs(MigrateCtx); 355 } 356 357 void MigrationContext::dumpGCAttrs() { 358 llvm::errs() << "\n################\n"; 359 for (unsigned i = 0, e = GCAttrs.size(); i != e; ++i) { 360 GCAttrOccurrence &Attr = GCAttrs[i]; 361 llvm::errs() << "KIND: " 362 << (Attr.Kind == GCAttrOccurrence::Strong ? "strong" : "weak"); 363 llvm::errs() << "\nLOC: "; 364 Attr.Loc.dump(Pass.Ctx.getSourceManager()); 365 llvm::errs() << "\nTYPE: "; 366 Attr.ModifiedType.dump(); 367 if (Attr.Dcl) { 368 llvm::errs() << "DECL:\n"; 369 Attr.Dcl->dump(); 370 } else { 371 llvm::errs() << "DECL: NONE"; 372 } 373 llvm::errs() << "\nMIGRATABLE: " << Attr.FullyMigratable; 374 llvm::errs() << "\n----------------\n"; 375 } 376 llvm::errs() << "\n################\n"; 377 } 378