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";
|
||
|
}
|