360 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			360 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //===--- TransGCAttrs.cpp - Transformations to ARC mode --------------------===//
 | |
| //
 | |
| //                     The LLVM Compiler Infrastructure
 | |
| //
 | |
| // This file is distributed under the University of Illinois Open Source
 | |
| // License. See LICENSE.TXT for details.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "Transforms.h"
 | |
| #include "Internals.h"
 | |
| #include "clang/AST/ASTContext.h"
 | |
| #include "clang/Basic/SourceManager.h"
 | |
| #include "clang/Lex/Lexer.h"
 | |
| #include "clang/Sema/SemaDiagnostic.h"
 | |
| #include "llvm/ADT/SmallString.h"
 | |
| #include "llvm/ADT/TinyPtrVector.h"
 | |
| #include "llvm/Support/SaveAndRestore.h"
 | |
| 
 | |
| using namespace clang;
 | |
| using namespace arcmt;
 | |
| using namespace trans;
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| /// \brief Collects all the places where GC attributes __strong/__weak occur.
 | |
| class GCAttrsCollector : public RecursiveASTVisitor<GCAttrsCollector> {
 | |
|   MigrationContext &MigrateCtx;
 | |
|   bool FullyMigratable;
 | |
|   std::vector<ObjCPropertyDecl *> &AllProps;
 | |
| 
 | |
|   typedef RecursiveASTVisitor<GCAttrsCollector> base;
 | |
| public:
 | |
|   GCAttrsCollector(MigrationContext &ctx,
 | |
|                    std::vector<ObjCPropertyDecl *> &AllProps)
 | |
|     : MigrateCtx(ctx), FullyMigratable(false),
 | |
|       AllProps(AllProps) { }
 | |
| 
 | |
|   bool shouldWalkTypesOfTypeLocs() const { return false; }
 | |
| 
 | |
|   bool VisitAttributedTypeLoc(AttributedTypeLoc TL) {
 | |
|     handleAttr(TL);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   bool TraverseDecl(Decl *D) {
 | |
|     if (!D || D->isImplicit())
 | |
|       return true;
 | |
| 
 | |
|     SaveAndRestore<bool> Save(FullyMigratable, isMigratable(D));
 | |
|     
 | |
|     if (ObjCPropertyDecl *PropD = dyn_cast<ObjCPropertyDecl>(D)) {
 | |
|       lookForAttribute(PropD, PropD->getTypeSourceInfo());
 | |
|       AllProps.push_back(PropD);
 | |
|     } else if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(D)) {
 | |
|       lookForAttribute(DD, DD->getTypeSourceInfo());
 | |
|     }
 | |
|     return base::TraverseDecl(D);
 | |
|   }
 | |
| 
 | |
|   void lookForAttribute(Decl *D, TypeSourceInfo *TInfo) {
 | |
|     if (!TInfo)
 | |
|       return;
 | |
|     TypeLoc TL = TInfo->getTypeLoc();
 | |
|     while (TL) {
 | |
|       if (QualifiedTypeLoc QL = TL.getAs<QualifiedTypeLoc>()) {
 | |
|         TL = QL.getUnqualifiedLoc();
 | |
|       } else if (AttributedTypeLoc Attr = TL.getAs<AttributedTypeLoc>()) {
 | |
|         if (handleAttr(Attr, D))
 | |
|           break;
 | |
|         TL = Attr.getModifiedLoc();
 | |
|       } else if (ArrayTypeLoc Arr = TL.getAs<ArrayTypeLoc>()) {
 | |
|         TL = Arr.getElementLoc();
 | |
|       } else if (PointerTypeLoc PT = TL.getAs<PointerTypeLoc>()) {
 | |
|         TL = PT.getPointeeLoc();
 | |
|       } else if (ReferenceTypeLoc RT = TL.getAs<ReferenceTypeLoc>())
 | |
|         TL = RT.getPointeeLoc();
 | |
|       else
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   bool handleAttr(AttributedTypeLoc TL, Decl *D = 0) {
 | |
|     if (TL.getAttrKind() != AttributedType::attr_objc_ownership)
 | |
|       return false;
 | |
| 
 | |
|     SourceLocation Loc = TL.getAttrNameLoc();
 | |
|     unsigned RawLoc = Loc.getRawEncoding();
 | |
|     if (MigrateCtx.AttrSet.count(RawLoc))
 | |
|       return true;
 | |
| 
 | |
|     ASTContext &Ctx = MigrateCtx.Pass.Ctx;
 | |
|     SourceManager &SM = Ctx.getSourceManager();
 | |
|     if (Loc.isMacroID())
 | |
|       Loc = SM.getImmediateExpansionRange(Loc).first;
 | |
|     SmallString<32> Buf;
 | |
|     bool Invalid = false;
 | |
|     StringRef Spell = Lexer::getSpelling(
 | |
|                                   SM.getSpellingLoc(TL.getAttrEnumOperandLoc()),
 | |
|                                   Buf, SM, Ctx.getLangOpts(), &Invalid);
 | |
|     if (Invalid)
 | |
|       return false;
 | |
|     MigrationContext::GCAttrOccurrence::AttrKind Kind;
 | |
|     if (Spell == "strong")
 | |
|       Kind = MigrationContext::GCAttrOccurrence::Strong;
 | |
|     else if (Spell == "weak")
 | |
|       Kind = MigrationContext::GCAttrOccurrence::Weak;
 | |
|     else
 | |
|       return false;
 | |
|  
 | |
|     MigrateCtx.AttrSet.insert(RawLoc);
 | |
|     MigrateCtx.GCAttrs.push_back(MigrationContext::GCAttrOccurrence());
 | |
|     MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs.back();
 | |
| 
 | |
|     Attr.Kind = Kind;
 | |
|     Attr.Loc = Loc;
 | |
|     Attr.ModifiedType = TL.getModifiedLoc().getType();
 | |
|     Attr.Dcl = D;
 | |
|     Attr.FullyMigratable = FullyMigratable;
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   bool isMigratable(Decl *D) {
 | |
|     if (isa<TranslationUnitDecl>(D))
 | |
|       return false;
 | |
| 
 | |
|     if (isInMainFile(D))
 | |
|       return true;
 | |
| 
 | |
|     if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
 | |
|       return FD->hasBody();
 | |
| 
 | |
|     if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D))
 | |
|       return hasObjCImpl(ContD);
 | |
| 
 | |
|     if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
 | |
|       for (CXXRecordDecl::method_iterator
 | |
|              MI = RD->method_begin(), ME = RD->method_end(); MI != ME; ++MI) {
 | |
|         if (MI->isOutOfLine())
 | |
|           return true;
 | |
|       }
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     return isMigratable(cast<Decl>(D->getDeclContext()));
 | |
|   }
 | |
| 
 | |
|   static bool hasObjCImpl(Decl *D) {
 | |
|     if (!D)
 | |
|       return false;
 | |
|     if (ObjCContainerDecl *ContD = dyn_cast<ObjCContainerDecl>(D)) {
 | |
|       if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(ContD))
 | |
|         return ID->getImplementation() != 0;
 | |
|       if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(ContD))
 | |
|         return CD->getImplementation() != 0;
 | |
|       if (isa<ObjCImplDecl>(ContD))
 | |
|         return true;
 | |
|       return false;
 | |
|     }
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   bool isInMainFile(Decl *D) {
 | |
|     if (!D)
 | |
|       return false;
 | |
| 
 | |
|     for (Decl::redecl_iterator
 | |
|            I = D->redecls_begin(), E = D->redecls_end(); I != E; ++I)
 | |
|       if (!isInMainFile(I->getLocation()))
 | |
|         return false;
 | |
|     
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   bool isInMainFile(SourceLocation Loc) {
 | |
|     if (Loc.isInvalid())
 | |
|       return false;
 | |
| 
 | |
|     SourceManager &SM = MigrateCtx.Pass.Ctx.getSourceManager();
 | |
|     return SM.isInFileID(SM.getExpansionLoc(Loc), SM.getMainFileID());
 | |
|   }
 | |
| };
 | |
| 
 | |
| } // anonymous namespace
 | |
| 
 | |
| static void errorForGCAttrsOnNonObjC(MigrationContext &MigrateCtx) {
 | |
|   TransformActions &TA = MigrateCtx.Pass.TA;
 | |
| 
 | |
|   for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
 | |
|     MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
 | |
|     if (Attr.FullyMigratable && Attr.Dcl) {
 | |
|       if (Attr.ModifiedType.isNull())
 | |
|         continue;
 | |
|       if (!Attr.ModifiedType->isObjCRetainableType()) {
 | |
|         TA.reportError("GC managed memory will become unmanaged in ARC",
 | |
|                        Attr.Loc);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void checkWeakGCAttrs(MigrationContext &MigrateCtx) {
 | |
|   TransformActions &TA = MigrateCtx.Pass.TA;
 | |
| 
 | |
|   for (unsigned i = 0, e = MigrateCtx.GCAttrs.size(); i != e; ++i) {
 | |
|     MigrationContext::GCAttrOccurrence &Attr = MigrateCtx.GCAttrs[i];
 | |
|     if (Attr.Kind == MigrationContext::GCAttrOccurrence::Weak) {
 | |
|       if (Attr.ModifiedType.isNull() ||
 | |
|           !Attr.ModifiedType->isObjCRetainableType())
 | |
|         continue;
 | |
|       if (!canApplyWeak(MigrateCtx.Pass.Ctx, Attr.ModifiedType,
 | |
|                         /*AllowOnUnknownClass=*/true)) {
 | |
|         Transaction Trans(TA);
 | |
|         if (!MigrateCtx.RemovedAttrSet.count(Attr.Loc.getRawEncoding()))
 | |
|           TA.replaceText(Attr.Loc, "__weak", "__unsafe_unretained");
 | |
|         TA.clearDiagnostic(diag::err_arc_weak_no_runtime,
 | |
|                            diag::err_arc_unsupported_weak_class,
 | |
|                            Attr.Loc);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
 | |
| 
 | |
| static void checkAllAtProps(MigrationContext &MigrateCtx,
 | |
|                             SourceLocation AtLoc,
 | |
|                             IndivPropsTy &IndProps) {
 | |
|   if (IndProps.empty())
 | |
|     return;
 | |
| 
 | |
|   for (IndivPropsTy::iterator
 | |
|          PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
 | |
|     QualType T = (*PI)->getType();
 | |
|     if (T.isNull() || !T->isObjCRetainableType())
 | |
|       return;
 | |
|   }
 | |
| 
 | |
|   SmallVector<std::pair<AttributedTypeLoc, ObjCPropertyDecl *>, 4> ATLs;
 | |
|   bool hasWeak = false, hasStrong = false;
 | |
|   ObjCPropertyDecl::PropertyAttributeKind
 | |
|     Attrs = ObjCPropertyDecl::OBJC_PR_noattr;
 | |
|   for (IndivPropsTy::iterator
 | |
|          PI = IndProps.begin(), PE = IndProps.end(); PI != PE; ++PI) {
 | |
|     ObjCPropertyDecl *PD = *PI;
 | |
|     Attrs = PD->getPropertyAttributesAsWritten();
 | |
|     TypeSourceInfo *TInfo = PD->getTypeSourceInfo();
 | |
|     if (!TInfo)
 | |
|       return;
 | |
|     TypeLoc TL = TInfo->getTypeLoc();
 | |
|     if (AttributedTypeLoc ATL =
 | |
|             TL.getAs<AttributedTypeLoc>()) {
 | |
|       ATLs.push_back(std::make_pair(ATL, PD));
 | |
|       if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Weak) {
 | |
|         hasWeak = true;
 | |
|       } else if (TInfo->getType().getObjCLifetime() == Qualifiers::OCL_Strong)
 | |
|         hasStrong = true;
 | |
|       else
 | |
|         return;
 | |
|     }
 | |
|   }
 | |
|   if (ATLs.empty())
 | |
|     return;
 | |
|   if (hasWeak && hasStrong)
 | |
|     return;
 | |
| 
 | |
|   TransformActions &TA = MigrateCtx.Pass.TA;
 | |
|   Transaction Trans(TA);
 | |
| 
 | |
|   if (GCAttrsCollector::hasObjCImpl(
 | |
|                               cast<Decl>(IndProps.front()->getDeclContext()))) {
 | |
|     if (hasWeak)
 | |
|       MigrateCtx.AtPropsWeak.insert(AtLoc.getRawEncoding());
 | |
| 
 | |
|   } else {
 | |
|     StringRef toAttr = "strong";
 | |
|     if (hasWeak) {
 | |
|       if (canApplyWeak(MigrateCtx.Pass.Ctx, IndProps.front()->getType(),
 | |
|                        /*AllowOnUnkwownClass=*/true))
 | |
|         toAttr = "weak";
 | |
|       else
 | |
|         toAttr = "unsafe_unretained";
 | |
|     }
 | |
|     if (Attrs & ObjCPropertyDecl::OBJC_PR_assign)
 | |
|       MigrateCtx.rewritePropertyAttribute("assign", toAttr, AtLoc);
 | |
|     else
 | |
|       MigrateCtx.addPropertyAttribute(toAttr, AtLoc);
 | |
|   }
 | |
| 
 | |
|   for (unsigned i = 0, e = ATLs.size(); i != e; ++i) {
 | |
|     SourceLocation Loc = ATLs[i].first.getAttrNameLoc();
 | |
|     if (Loc.isMacroID())
 | |
|       Loc = MigrateCtx.Pass.Ctx.getSourceManager()
 | |
|                                          .getImmediateExpansionRange(Loc).first;
 | |
|     TA.remove(Loc);
 | |
|     TA.clearDiagnostic(diag::err_objc_property_attr_mutually_exclusive, AtLoc);
 | |
|     TA.clearDiagnostic(diag::err_arc_inconsistent_property_ownership,
 | |
|                        ATLs[i].second->getLocation());
 | |
|     MigrateCtx.RemovedAttrSet.insert(Loc.getRawEncoding());
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void checkAllProps(MigrationContext &MigrateCtx,
 | |
|                           std::vector<ObjCPropertyDecl *> &AllProps) {
 | |
|   typedef llvm::TinyPtrVector<ObjCPropertyDecl *> IndivPropsTy;
 | |
|   llvm::DenseMap<unsigned, IndivPropsTy> AtProps;
 | |
| 
 | |
|   for (unsigned i = 0, e = AllProps.size(); i != e; ++i) {
 | |
|     ObjCPropertyDecl *PD = AllProps[i];
 | |
|     if (PD->getPropertyAttributesAsWritten() &
 | |
|           (ObjCPropertyDecl::OBJC_PR_assign |
 | |
|            ObjCPropertyDecl::OBJC_PR_readonly)) {
 | |
|       SourceLocation AtLoc = PD->getAtLoc();
 | |
|       if (AtLoc.isInvalid())
 | |
|         continue;
 | |
|       unsigned RawAt = AtLoc.getRawEncoding();
 | |
|       AtProps[RawAt].push_back(PD);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   for (llvm::DenseMap<unsigned, IndivPropsTy>::iterator
 | |
|          I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
 | |
|     SourceLocation AtLoc = SourceLocation::getFromRawEncoding(I->first);
 | |
|     IndivPropsTy &IndProps = I->second;
 | |
|     checkAllAtProps(MigrateCtx, AtLoc, IndProps);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void GCAttrsTraverser::traverseTU(MigrationContext &MigrateCtx) {
 | |
|   std::vector<ObjCPropertyDecl *> AllProps;
 | |
|   GCAttrsCollector(MigrateCtx, AllProps).TraverseDecl(
 | |
|                                   MigrateCtx.Pass.Ctx.getTranslationUnitDecl());
 | |
| 
 | |
|   errorForGCAttrsOnNonObjC(MigrateCtx);
 | |
|   checkAllProps(MigrateCtx, AllProps);
 | |
|   checkWeakGCAttrs(MigrateCtx);
 | |
| }
 | |
| 
 | |
| void MigrationContext::dumpGCAttrs() {
 | |
|   llvm::errs() << "\n################\n";
 | |
|   for (unsigned i = 0, e = GCAttrs.size(); i != e; ++i) {
 | |
|     GCAttrOccurrence &Attr = GCAttrs[i];
 | |
|     llvm::errs() << "KIND: "
 | |
|         << (Attr.Kind == GCAttrOccurrence::Strong ? "strong" : "weak");
 | |
|     llvm::errs() << "\nLOC: ";
 | |
|     Attr.Loc.dump(Pass.Ctx.getSourceManager());
 | |
|     llvm::errs() << "\nTYPE: ";
 | |
|     Attr.ModifiedType.dump();
 | |
|     if (Attr.Dcl) {
 | |
|       llvm::errs() << "DECL:\n";
 | |
|       Attr.Dcl->dump();
 | |
|     } else {
 | |
|       llvm::errs() << "DECL: NONE";
 | |
|     }
 | |
|     llvm::errs() << "\nMIGRATABLE: " << Attr.FullyMigratable;
 | |
|     llvm::errs() << "\n----------------\n";
 | |
|   }
 | |
|   llvm::errs() << "\n################\n";
 | |
| }
 | 
