Automatic Reference Counting.
Language-design credit goes to a lot of people, but I particularly want
to single out Blaine Garst and Patrick Beard for their contributions.
Compiler implementation credit goes to Argyrios, Doug, Fariborz, and myself,
in no particular order.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@133103 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 4c66ed8..fac2eac 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -221,9 +221,9 @@
DependentTemplateSpecializationTypes(this_()),
GlobalNestedNameSpecifier(0), IsInt128Installed(false),
CFConstantStringTypeDecl(0), NSConstantStringTypeDecl(0),
- ObjCFastEnumerationStateTypeDecl(0), FILEDecl(0), jmp_bufDecl(0),
- sigjmp_bufDecl(0), BlockDescriptorType(0), BlockDescriptorExtendedType(0),
- cudaConfigureCallDecl(0),
+ ObjCFastEnumerationStateTypeDecl(0), FILEDecl(0),
+ jmp_bufDecl(0), sigjmp_bufDecl(0), BlockDescriptorType(0),
+ BlockDescriptorExtendedType(0), cudaConfigureCallDecl(0),
NullTypeSourceInfo(QualType()),
SourceMgr(SM), LangOpts(LOpts), ABI(createCXXABI(t)),
AddrSpaceMap(getAddressSpaceMap(t, LOpts)), Target(t),
@@ -2040,10 +2040,13 @@
assert(NewIP == 0 && "Shouldn't be in the map!"); (void)NewIP;
}
- // FunctionProtoType objects are allocated with extra bytes after them
- // for two variable size arrays (for parameter and exception types) at the
- // end of them. Instead of the exception types, there could be a noexcept
- // expression and a context pointer.
+ // FunctionProtoType objects are allocated with extra bytes after
+ // them for three variable size arrays at the end:
+ // - parameter types
+ // - exception types
+ // - consumed-arguments flags
+ // Instead of the exception types, there could be a noexcept
+ // expression.
size_t Size = sizeof(FunctionProtoType) +
NumArgs * sizeof(QualType);
if (EPI.ExceptionSpecType == EST_Dynamic)
@@ -2051,6 +2054,9 @@
else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) {
Size += sizeof(Expr*);
}
+ if (EPI.ConsumedArguments)
+ Size += NumArgs * sizeof(bool);
+
FunctionProtoType *FTP = (FunctionProtoType*) Allocate(Size, TypeAlignment);
FunctionProtoType::ExtProtoInfo newEPI = EPI;
newEPI.ExtInfo = EPI.ExtInfo.withCallingConv(CallConv);
@@ -2925,7 +2931,6 @@
return CanQualType::CreateUnsafe(Result);
}
-
QualType ASTContext::getUnqualifiedArrayType(QualType type,
Qualifiers &quals) {
SplitQualType splitType = type.getSplitUnqualifiedType();
@@ -3725,11 +3730,7 @@
}
bool ASTContext::BlockRequiresCopying(QualType Ty) const {
- if (Ty->isBlockPointerType())
- return true;
- if (isObjCNSObjectType(Ty))
- return true;
- if (Ty->isObjCObjectPointerType())
+ if (Ty->isObjCRetainableType())
return true;
if (getLangOptions().CPlusPlus) {
if (const RecordType *RT = Ty->getAs<RecordType>()) {
@@ -4826,20 +4827,6 @@
// Type Predicates.
//===----------------------------------------------------------------------===//
-/// isObjCNSObjectType - Return true if this is an NSObject object using
-/// NSObject attribute on a c-style pointer type.
-/// FIXME - Make it work directly on types.
-/// FIXME: Move to Type.
-///
-bool ASTContext::isObjCNSObjectType(QualType Ty) const {
- if (const TypedefType *TDT = dyn_cast<TypedefType>(Ty)) {
- if (TypedefNameDecl *TD = TDT->getDecl())
- if (TD->getAttr<ObjCNSObjectAttr>())
- return true;
- }
- return false;
-}
-
/// getObjCGCAttr - Returns one of GCNone, Weak or Strong objc's
/// garbage collection attribute.
///
@@ -5452,6 +5439,9 @@
if (lbaseInfo.getRegParm() != rbaseInfo.getRegParm())
return QualType();
+ if (lbaseInfo.getProducesResult() != rbaseInfo.getProducesResult())
+ return QualType();
+
// It's noreturn if either type is.
// FIXME: some uses, e.g. conditional exprs, really want this to be 'both'.
bool NoReturn = lbaseInfo.getNoReturn() || rbaseInfo.getNoReturn();
@@ -5460,10 +5450,7 @@
if (NoReturn != rbaseInfo.getNoReturn())
allRTypes = false;
- FunctionType::ExtInfo einfo(NoReturn,
- lbaseInfo.getHasRegParm(),
- lbaseInfo.getRegParm(),
- lbaseInfo.getCC());
+ FunctionType::ExtInfo einfo = lbaseInfo.withNoReturn(NoReturn);
if (lproto && rproto) { // two C99 style function prototypes
assert(!lproto->hasExceptionSpec() && !rproto->hasExceptionSpec() &&
@@ -5584,7 +5571,8 @@
// If any of these qualifiers are different, we have a type
// mismatch.
if (LQuals.getCVRQualifiers() != RQuals.getCVRQualifiers() ||
- LQuals.getAddressSpace() != RQuals.getAddressSpace())
+ LQuals.getAddressSpace() != RQuals.getAddressSpace() ||
+ LQuals.getObjCLifetime() != RQuals.getObjCLifetime())
return QualType();
// Exactly one GC qualifier difference is allowed: __strong is
diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp
index 08ac2a5..c10e6c4 100644
--- a/lib/AST/DeclCXX.cpp
+++ b/lib/AST/DeclCXX.cpp
@@ -228,6 +228,11 @@
if (!BaseClassDecl->hasTrivialDestructor())
data().HasTrivialDestructor = false;
+ // A class has an Objective-C object member if... or any of its bases
+ // has an Objective-C object member.
+ if (BaseClassDecl->hasObjectMember())
+ setHasObjectMember(true);
+
// Keep track of the presence of mutable fields.
if (BaseClassDecl->hasMutableFields())
data().HasMutableFields = true;
@@ -698,10 +703,23 @@
// A POD struct is a class that is both a trivial class and a
// standard-layout class, and has no non-static data members of type
// non-POD struct, non-POD union (or array of such types).
+ //
+ // Automatic Reference Counting: the presence of a member of Objective-C pointer type
+ // that does not explicitly have no lifetime makes the class a non-POD.
+ // However, we delay setting PlainOldData to false in this case so that
+ // Sema has a chance to diagnostic causes where the same class will be
+ // non-POD with Automatic Reference Counting but a POD without Instant Objects.
+ // In this case, the class will become a non-POD class when we complete
+ // the definition.
ASTContext &Context = getASTContext();
QualType T = Context.getBaseElementType(Field->getType());
- if (!T->isPODType())
+ if (T->isObjCRetainableType() || T.isObjCGCStrong()) {
+ if (!Context.getLangOptions().ObjCAutoRefCount ||
+ T.getObjCLifetime() != Qualifiers::OCL_ExplicitNone)
+ setHasObjectMember(true);
+ } else if (!T.isPODType(Context))
data().PlainOldData = false;
+
if (T->isReferenceType()) {
data().HasTrivialDefaultConstructor = false;
@@ -768,6 +786,8 @@
if (!FieldRec->hasTrivialDestructor())
data().HasTrivialDestructor = false;
+ if (FieldRec->hasObjectMember())
+ setHasObjectMember(true);
// C++0x [class]p7:
// A standard-layout class is a class that:
@@ -1078,6 +1098,20 @@
void CXXRecordDecl::completeDefinition(CXXFinalOverriderMap *FinalOverriders) {
RecordDecl::completeDefinition();
+ if (hasObjectMember() && getASTContext().getLangOptions().ObjCAutoRefCount) {
+ // Objective-C Automatic Reference Counting:
+ // If a class has a non-static data member of Objective-C pointer
+ // type (or array thereof), it is a non-POD type and its
+ // default constructor (if any), copy constructor, copy assignment
+ // operator, and destructor are non-trivial.
+ struct DefinitionData &Data = data();
+ Data.PlainOldData = false;
+ Data.HasTrivialDefaultConstructor = false;
+ Data.HasTrivialCopyConstructor = false;
+ Data.HasTrivialCopyAssignment = false;
+ Data.HasTrivialDestructor = false;
+ }
+
// If the class may be abstract (but hasn't been marked as such), check for
// any pure final overriders.
if (mayBeAbstract()) {
diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp
index e2c4f38..99eb0d3 100644
--- a/lib/AST/DeclObjC.cpp
+++ b/lib/AST/DeclObjC.cpp
@@ -474,8 +474,28 @@
} else // we have a factory method.
selfTy = Context.getObjCClassType();
- setSelfDecl(ImplicitParamDecl::Create(Context, this, SourceLocation(),
- &Context.Idents.get("self"), selfTy));
+ bool selfIsConsumed = false;
+ if (isInstanceMethod() && Context.getLangOptions().ObjCAutoRefCount) {
+ selfIsConsumed = hasAttr<NSConsumesSelfAttr>();
+
+ // 'self' is always __strong, although as a special case we don't
+ // actually retain it except in init methods.
+ Qualifiers qs;
+ qs.setObjCLifetime(Qualifiers::OCL_Strong);
+ selfTy = Context.getQualifiedType(selfTy, qs);
+
+ // In addition, 'self' is const unless this is an init method.
+ if (getMethodFamily() != OMF_init)
+ selfTy = selfTy.withConst();
+ }
+
+ ImplicitParamDecl *self
+ = ImplicitParamDecl::Create(Context, this, SourceLocation(),
+ &Context.Idents.get("self"), selfTy);
+ setSelfDecl(self);
+
+ if (selfIsConsumed)
+ self->addAttr(new (Context) NSConsumedAttr(SourceLocation(), Context));
setCmdDecl(ImplicitParamDecl::Create(Context, this, SourceLocation(),
&Context.Idents.get("_cmd"),
diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp
index 421770e..19554a3 100644
--- a/lib/AST/DeclPrinter.cpp
+++ b/lib/AST/DeclPrinter.cpp
@@ -933,6 +933,11 @@
first = false;
}
+ if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_strong) {
+ Out << (first ? ' ' : ',') << "strong";
+ first = false;
+ }
+
if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_copy) {
Out << (first ? ' ' : ',') << "copy";
first = false;
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index 9872139..97f4ea6 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -1045,6 +1045,10 @@
return "IntegralComplexCast";
case CK_IntegralComplexToFloatingComplex:
return "IntegralComplexToFloatingComplex";
+ case CK_ObjCConsumeObject:
+ return "ObjCConsumeObject";
+ case CK_ObjCProduceObject:
+ return "ObjCProduceObject";
}
llvm_unreachable("Unhandled cast kind!");
@@ -1490,6 +1494,17 @@
case ObjCMessageExprClass: {
const ObjCMessageExpr *ME = cast<ObjCMessageExpr>(this);
+ if (Ctx.getLangOptions().ObjCAutoRefCount &&
+ ME->isInstanceMessage() &&
+ !ME->getType()->isVoidType() &&
+ ME->getSelector().getIdentifierInfoForSlot(0) &&
+ ME->getSelector().getIdentifierInfoForSlot(0)
+ ->getName().startswith("init")) {
+ Loc = getExprLoc();
+ R1 = ME->getSourceRange();
+ return true;
+ }
+
const ObjCMethodDecl *MD = ME->getMethodDecl();
if (MD && MD->getAttr<WarnUnusedResultAttr>()) {
Loc = getExprLoc();
@@ -2519,7 +2534,7 @@
/*TypeDependent=*/false, /*ValueDependent=*/false,
/*ContainsUnexpandedParameterPack=*/false),
NumArgs(NumArgs), Kind(IsInstanceSuper? SuperInstance : SuperClass),
- HasMethod(Method != 0), SuperLoc(SuperLoc),
+ HasMethod(Method != 0), IsDelegateInitCall(false), SuperLoc(SuperLoc),
SelectorOrMethod(reinterpret_cast<uintptr_t>(Method? Method
: Sel.getAsOpaquePtr())),
SelectorLoc(SelLoc), LBracLoc(LBracLoc), RBracLoc(RBracLoc)
@@ -2540,7 +2555,8 @@
SourceLocation RBracLoc)
: Expr(ObjCMessageExprClass, T, VK, OK_Ordinary, T->isDependentType(),
T->isDependentType(), T->containsUnexpandedParameterPack()),
- NumArgs(NumArgs), Kind(Class), HasMethod(Method != 0),
+ NumArgs(NumArgs), Kind(Class),
+ HasMethod(Method != 0), IsDelegateInitCall(false),
SelectorOrMethod(reinterpret_cast<uintptr_t>(Method? Method
: Sel.getAsOpaquePtr())),
SelectorLoc(SelLoc), LBracLoc(LBracLoc), RBracLoc(RBracLoc)
@@ -2571,7 +2587,8 @@
: Expr(ObjCMessageExprClass, T, VK, OK_Ordinary, Receiver->isTypeDependent(),
Receiver->isTypeDependent(),
Receiver->containsUnexpandedParameterPack()),
- NumArgs(NumArgs), Kind(Instance), HasMethod(Method != 0),
+ NumArgs(NumArgs), Kind(Instance),
+ HasMethod(Method != 0), IsDelegateInitCall(false),
SelectorOrMethod(reinterpret_cast<uintptr_t>(Method? Method
: Sel.getAsOpaquePtr())),
SelectorLoc(SelLoc), LBracLoc(LBracLoc), RBracLoc(RBracLoc)
@@ -2702,6 +2719,19 @@
return 0;
}
+llvm::StringRef ObjCBridgedCastExpr::getBridgeKindName() const {
+ switch (getBridgeKind()) {
+ case OBC_Bridge:
+ return "__bridge";
+ case OBC_BridgeTransfer:
+ return "__bridge_transfer";
+ case OBC_BridgeRetained:
+ return "__bridge_retained";
+ }
+
+ return "__bridge";
+}
+
bool ChooseExpr::isConditionTrue(const ASTContext &C) const {
return getCond()->EvaluateAsInt(C) != 0;
}
diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp
index d177cb5..1a1fa91 100644
--- a/lib/AST/ExprClassification.cpp
+++ b/lib/AST/ExprClassification.cpp
@@ -162,6 +162,7 @@
case Expr::SizeOfPackExprClass:
case Expr::SubstNonTypeTemplateParmPackExprClass:
case Expr::AsTypeExprClass:
+ case Expr::ObjCIndirectCopyRestoreExprClass:
return Cl::CL_PRValue;
// Next come the complicated cases.
@@ -289,6 +290,7 @@
case Expr::CXXDynamicCastExprClass:
case Expr::CXXReinterpretCastExprClass:
case Expr::CXXConstCastExprClass:
+ case Expr::ObjCBridgedCastExprClass:
// Only in C++ can casts be interesting at all.
if (!Lang.CPlusPlus) return Cl::CL_PRValue;
return ClassifyUnnamed(Ctx, cast<ExplicitCastExpr>(E)->getTypeAsWritten());
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index 06c5645..432ffee 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -282,6 +282,17 @@
return true;
return false;
}
+ bool VisitObjCIvarRefExpr(const ObjCIvarRefExpr *E) {
+ if (Info.Ctx.getCanonicalType(E->getType()).isVolatileQualified())
+ return true;
+ return false;
+ }
+ bool VisitBlockDeclRefExpr (const BlockDeclRefExpr *E) {
+ if (Info.Ctx.getCanonicalType(E->getType()).isVolatileQualified())
+ return true;
+ return false;
+ }
+
// We don't want to evaluate BlockExprs multiple times, as they generate
// a ton of code.
bool VisitBlockExpr(const BlockExpr *E) { return true; }
@@ -1797,6 +1808,8 @@
case CK_GetObjCProperty:
case CK_LValueBitCast:
case CK_UserDefinedConversion:
+ case CK_ObjCProduceObject:
+ case CK_ObjCConsumeObject:
return false;
case CK_LValueToRValue:
@@ -2301,6 +2314,8 @@
case CK_FloatingComplexToBoolean:
case CK_IntegralComplexToReal:
case CK_IntegralComplexToBoolean:
+ case CK_ObjCProduceObject:
+ case CK_ObjCConsumeObject:
llvm_unreachable("invalid cast kind for complex value");
case CK_LValueToRValue:
@@ -2771,6 +2786,7 @@
case Expr::PackExpansionExprClass:
case Expr::SubstNonTypeTemplateParmPackExprClass:
case Expr::AsTypeExprClass:
+ case Expr::ObjCIndirectCopyRestoreExprClass:
return ICEDiag(2, E->getLocStart());
case Expr::SizeOfPackExprClass:
@@ -2995,7 +3011,8 @@
case Expr::CXXFunctionalCastExprClass:
case Expr::CXXStaticCastExprClass:
case Expr::CXXReinterpretCastExprClass:
- case Expr::CXXConstCastExprClass: {
+ case Expr::CXXConstCastExprClass:
+ case Expr::ObjCBridgedCastExprClass: {
const Expr *SubExpr = cast<CastExpr>(E)->getSubExpr();
if (SubExpr->getType()->isIntegralOrEnumerationType())
return CheckICE(SubExpr, Ctx);
diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp
index e81ec7e..a77fe5f 100644
--- a/lib/AST/ItaniumMangle.cpp
+++ b/lib/AST/ItaniumMangle.cpp
@@ -21,6 +21,7 @@
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprObjC.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/ABI.h"
#include "clang/Basic/SourceManager.h"
@@ -1464,7 +1465,35 @@
Out << 'U' << ASString.size() << ASString;
}
- // FIXME: For now, just drop all extension qualifiers on the floor.
+ llvm::StringRef LifetimeName;
+ switch (Quals.getObjCLifetime()) {
+ // Objective-C ARC Extension:
+ //
+ // <type> ::= U "__strong"
+ // <type> ::= U "__weak"
+ // <type> ::= U "__autoreleasing"
+ // <type> ::= U "__unsafe_unretained"
+ case Qualifiers::OCL_None:
+ break;
+
+ case Qualifiers::OCL_Weak:
+ LifetimeName = "__weak";
+ break;
+
+ case Qualifiers::OCL_Strong:
+ LifetimeName = "__strong";
+ break;
+
+ case Qualifiers::OCL_Autoreleasing:
+ LifetimeName = "__autoreleasing";
+ break;
+
+ case Qualifiers::OCL_ExplicitNone:
+ LifetimeName = "__unsafe_unretained";
+ break;
+ }
+ if (!LifetimeName.empty())
+ Out << 'U' << LifetimeName.size() << LifetimeName;
}
void CXXNameMangler::mangleRefQualifier(RefQualifierKind RefQualifier) {
@@ -2089,6 +2118,7 @@
case Expr::ObjCProtocolExprClass:
case Expr::ObjCSelectorExprClass:
case Expr::ObjCStringLiteralClass:
+ case Expr::ObjCIndirectCopyRestoreExprClass:
case Expr::OffsetOfExprClass:
case Expr::PredefinedExprClass:
case Expr::ShuffleVectorExprClass:
@@ -2347,7 +2377,15 @@
mangleExpression(cast<ImplicitCastExpr>(E)->getSubExpr(), Arity);
break;
}
-
+
+ case Expr::ObjCBridgedCastExprClass: {
+ // Mangle ownership casts as a vendor extended operator __bridge,
+ // __bridge_transfer, or __bridge_retain.
+ llvm::StringRef Kind = cast<ObjCBridgedCastExpr>(E)->getBridgeKindName();
+ Out << "v1U" << Kind.size() << Kind;
+ }
+ // Fall through to mangle the cast itself.
+
case Expr::CStyleCastExprClass:
case Expr::CXXStaticCastExprClass:
case Expr::CXXDynamicCastExprClass:
diff --git a/lib/AST/ParentMap.cpp b/lib/AST/ParentMap.cpp
index eca351a..b7b2005 100644
--- a/lib/AST/ParentMap.cpp
+++ b/lib/AST/ParentMap.cpp
@@ -66,6 +66,15 @@
return S;
}
+Stmt *ParentMap::getOuterParenParent(Stmt *S) const {
+ Stmt *Paren = 0;
+ while (isa<ParenExpr>(S)) {
+ Paren = S;
+ S = getParent(S);
+ };
+ return Paren;
+}
+
bool ParentMap::isConsumedExpr(Expr* E) const {
Stmt *P = getParent(E);
Stmt *DirectChild = E;
diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp
index 87588e4..d6a67b1 100644
--- a/lib/AST/StmtPrinter.cpp
+++ b/lib/AST/StmtPrinter.cpp
@@ -449,6 +449,12 @@
OS << "\n";
}
+void StmtPrinter::VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *Node) {
+ Indent() << "@autoreleasepool";
+ PrintRawCompoundStmt(dyn_cast<CompoundStmt>(Node->getSubStmt()));
+ OS << "\n";
+}
+
void StmtPrinter::PrintRawCXXCatchStmt(CXXCatchStmt *Node) {
OS << "catch (";
if (Decl *ExDecl = Node->getExceptionDecl())
@@ -1464,6 +1470,17 @@
OS << "]";
}
+void
+StmtPrinter::VisitObjCIndirectCopyRestoreExpr(ObjCIndirectCopyRestoreExpr *E) {
+ PrintExpr(E->getSubExpr());
+}
+
+void
+StmtPrinter::VisitObjCBridgedCastExpr(ObjCBridgedCastExpr *E) {
+ OS << "(" << E->getBridgeKindName() << E->getType().getAsString(Policy)
+ << ")";
+ PrintExpr(E->getSubExpr());
+}
void StmtPrinter::VisitBlockExpr(BlockExpr *Node) {
BlockDecl *BD = Node->getBlockDecl();
diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp
index b117cd9..c70c87a 100644
--- a/lib/AST/StmtProfile.cpp
+++ b/lib/AST/StmtProfile.cpp
@@ -220,6 +220,10 @@
VisitStmt(S);
}
+void StmtProfiler::VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) {
+ VisitStmt(S);
+}
+
void StmtProfiler::VisitExpr(Expr *S) {
VisitStmt(S);
}
@@ -952,6 +956,17 @@
ID.AddBoolean(S->isArrow());
}
+void
+StmtProfiler::VisitObjCIndirectCopyRestoreExpr(ObjCIndirectCopyRestoreExpr *S) {
+ VisitExpr(S);
+ ID.AddBoolean(S->shouldCopy());
+}
+
+void StmtProfiler::VisitObjCBridgedCastExpr(ObjCBridgedCastExpr *S) {
+ VisitExplicitCastExpr(S);
+ ID.AddBoolean(S->getBridgeKind());
+}
+
void StmtProfiler::VisitDecl(Decl *D) {
ID.AddInteger(D? D->getKind() : 0);
diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp
index 938a686..080bca2 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -36,7 +36,10 @@
(hasObjCGCAttr() && !Other.hasObjCGCAttr())) &&
// Address space superset.
((getAddressSpace() == Other.getAddressSpace()) ||
- (hasAddressSpace()&& !Other.hasAddressSpace()));
+ (hasAddressSpace()&& !Other.hasAddressSpace())) &&
+ // Lifetime qualifier superset.
+ ((getObjCLifetime() == Other.getObjCLifetime()) ||
+ (hasObjCLifetime() && !Other.hasObjCLifetime()));
}
bool QualType::isConstant(QualType T, ASTContext &Ctx) {
@@ -866,39 +869,59 @@
}
}
-/// isPODType - Return true if this is a plain-old-data type (C++ 3.9p10)
-bool Type::isPODType() const {
+bool QualType::isPODType(ASTContext &Context) const {
// The compiler shouldn't query this for incomplete types, but the user might.
// We return false for that case. Except for incomplete arrays of PODs, which
// are PODs according to the standard.
- if (isIncompleteArrayType() &&
- cast<ArrayType>(CanonicalType)->getElementType()->isPODType())
- return true;
- if (isIncompleteType())
+ if (isNull())
+ return 0;
+
+ if ((*this)->isIncompleteArrayType())
+ return Context.getBaseElementType(*this).isPODType(Context);
+
+ if ((*this)->isIncompleteType())
return false;
+ if (Context.getLangOptions().ObjCAutoRefCount) {
+ switch (getObjCLifetime()) {
+ case Qualifiers::OCL_ExplicitNone:
+ return true;
+
+ case Qualifiers::OCL_Strong:
+ case Qualifiers::OCL_Weak:
+ case Qualifiers::OCL_Autoreleasing:
+ return false;
+
+ case Qualifiers::OCL_None:
+ if ((*this)->isObjCLifetimeType())
+ return false;
+ break;
+ }
+ }
+
+ QualType CanonicalType = getTypePtr()->CanonicalType;
switch (CanonicalType->getTypeClass()) {
// Everything not explicitly mentioned is not POD.
default: return false;
- case VariableArray:
- case ConstantArray:
+ case Type::VariableArray:
+ case Type::ConstantArray:
// IncompleteArray is handled above.
- return cast<ArrayType>(CanonicalType)->getElementType()->isPODType();
-
- case Builtin:
- case Complex:
- case Pointer:
- case MemberPointer:
- case Vector:
- case ExtVector:
- case ObjCObjectPointer:
- case BlockPointer:
+ return Context.getBaseElementType(*this).isPODType(Context);
+
+ case Type::ObjCObjectPointer:
+ case Type::BlockPointer:
+ case Type::Builtin:
+ case Type::Complex:
+ case Type::Pointer:
+ case Type::MemberPointer:
+ case Type::Vector:
+ case Type::ExtVector:
return true;
- case Enum:
+ case Type::Enum:
return true;
- case Record:
+ case Type::Record:
if (CXXRecordDecl *ClassDecl
= dyn_cast<CXXRecordDecl>(cast<RecordType>(CanonicalType)->getDecl()))
return ClassDecl->isPOD();
@@ -908,6 +931,121 @@
}
}
+bool QualType::isTrivialType(ASTContext &Context) const {
+ // The compiler shouldn't query this for incomplete types, but the user might.
+ // We return false for that case. Except for incomplete arrays of PODs, which
+ // are PODs according to the standard.
+ if (isNull())
+ return 0;
+
+ if ((*this)->isArrayType())
+ return Context.getBaseElementType(*this).isTrivialType(Context);
+
+ // Return false for incomplete types after skipping any incomplete array
+ // types which are expressly allowed by the standard and thus our API.
+ if ((*this)->isIncompleteType())
+ return false;
+
+ if (Context.getLangOptions().ObjCAutoRefCount) {
+ switch (getObjCLifetime()) {
+ case Qualifiers::OCL_ExplicitNone:
+ return true;
+
+ case Qualifiers::OCL_Strong:
+ case Qualifiers::OCL_Weak:
+ case Qualifiers::OCL_Autoreleasing:
+ return false;
+
+ case Qualifiers::OCL_None:
+ if ((*this)->isObjCLifetimeType())
+ return false;
+ break;
+ }
+ }
+
+ QualType CanonicalType = getTypePtr()->CanonicalType;
+ if (CanonicalType->isDependentType())
+ return false;
+
+ // C++0x [basic.types]p9:
+ // Scalar types, trivial class types, arrays of such types, and
+ // cv-qualified versions of these types are collectively called trivial
+ // types.
+
+ // As an extension, Clang treats vector types as Scalar types.
+ if (CanonicalType->isScalarType() || CanonicalType->isVectorType())
+ return true;
+ if (const RecordType *RT = CanonicalType->getAs<RecordType>()) {
+ if (const CXXRecordDecl *ClassDecl =
+ dyn_cast<CXXRecordDecl>(RT->getDecl())) {
+ // C++0x [class]p5:
+ // A trivial class is a class that has a trivial default constructor
+ if (!ClassDecl->hasTrivialDefaultConstructor()) return false;
+ // and is trivially copyable.
+ if (!ClassDecl->isTriviallyCopyable()) return false;
+ }
+
+ return true;
+ }
+
+ // No other types can match.
+ return false;
+}
+
+bool QualType::isTriviallyCopyableType(ASTContext &Context) const {
+ if ((*this)->isArrayType())
+ return Context.getBaseElementType(*this).isTrivialType(Context);
+
+ if (Context.getLangOptions().ObjCAutoRefCount) {
+ switch (getObjCLifetime()) {
+ case Qualifiers::OCL_ExplicitNone:
+ return true;
+
+ case Qualifiers::OCL_Strong:
+ case Qualifiers::OCL_Weak:
+ case Qualifiers::OCL_Autoreleasing:
+ return false;
+
+ case Qualifiers::OCL_None:
+ if ((*this)->isObjCLifetimeType())
+ return false;
+ break;
+ }
+ }
+
+ // C++0x [basic.types]p9
+ // Scalar types, trivially copyable class types, arrays of such types, and
+ // cv-qualified versions of these types are collectively called trivial
+ // types.
+
+ QualType CanonicalType = getCanonicalType();
+ if (CanonicalType->isDependentType())
+ return false;
+
+ // Return false for incomplete types after skipping any incomplete array types
+ // which are expressly allowed by the standard and thus our API.
+ if (CanonicalType->isIncompleteType())
+ return false;
+
+ // As an extension, Clang treats vector types as Scalar types.
+ if (CanonicalType->isScalarType() || CanonicalType->isVectorType())
+ return true;
+
+ if (const RecordType *RT = CanonicalType->getAs<RecordType>()) {
+ if (const CXXRecordDecl *ClassDecl =
+ dyn_cast<CXXRecordDecl>(RT->getDecl())) {
+ if (!ClassDecl->isTriviallyCopyable()) return false;
+ }
+
+ return true;
+ }
+
+ // No other types can match.
+ return false;
+}
+
+
+
bool Type::isLiteralType() const {
if (isDependentType())
return false;
@@ -928,6 +1066,10 @@
if (BaseTy->isIncompleteType())
return false;
+ // Objective-C lifetime types are not literal types.
+ if (BaseTy->isObjCRetainableType())
+ return false;
+
// C++0x [basic.types]p10:
// A type is a literal type if it is:
// -- a scalar type; or
@@ -961,68 +1103,6 @@
return false;
}
-bool Type::isTrivialType() const {
- if (isDependentType())
- return false;
-
- // C++0x [basic.types]p9:
- // Scalar types, trivial class types, arrays of such types, and
- // cv-qualified versions of these types are collectively called trivial
- // types.
- const Type *BaseTy = getBaseElementTypeUnsafe();
- assert(BaseTy && "NULL element type");
-
- // Return false for incomplete types after skipping any incomplete array
- // types which are expressly allowed by the standard and thus our API.
- if (BaseTy->isIncompleteType())
- return false;
-
- // As an extension, Clang treats vector types as Scalar types.
- if (BaseTy->isScalarType() || BaseTy->isVectorType()) return true;
- if (const RecordType *RT = BaseTy->getAs<RecordType>()) {
- if (const CXXRecordDecl *ClassDecl =
- dyn_cast<CXXRecordDecl>(RT->getDecl())) {
- if (!ClassDecl->isTrivial()) return false;
- }
-
- return true;
- }
-
- // No other types can match.
- return false;
-}
-
-bool Type::isTriviallyCopyableType() const {
- if (isDependentType())
- return false;
-
- // C++0x [basic.types]p9
- // Scalar types, trivially copyable class types, arrays of such types, and
- // cv-qualified versions of these types are collectively called trivial
- // types.
- const Type *BaseTy = getBaseElementTypeUnsafe();
- assert(BaseTy && "NULL element type");
-
- // Return false for incomplete types after skipping any incomplete array types
- // which are expressly allowed by the standard and thus our API.
- if (BaseTy->isIncompleteType())
- return false;
-
- // As an extension, Clang treats vector types as Scalar types.
- if (BaseTy->isScalarType() || BaseTy->isVectorType()) return true;
- if (const RecordType *RT = BaseTy->getAs<RecordType>()) {
- if (const CXXRecordDecl *ClassDecl =
- dyn_cast<CXXRecordDecl>(RT->getDecl())) {
- if (!ClassDecl->isTriviallyCopyable()) return false;
- }
-
- return true;
- }
-
- // No other types can match.
- return false;
-}
-
bool Type::isStandardLayoutType() const {
if (isDependentType())
return false;
@@ -1060,14 +1140,32 @@
// This is effectively the intersection of isTrivialType and
// isStandardLayoutType. We implement it dircetly to avoid redundant
// conversions from a type to a CXXRecordDecl.
-bool Type::isCXX11PODType() const {
- if (isDependentType())
+bool QualType::isCXX11PODType(ASTContext &Context) const {
+ const Type *ty = getTypePtr();
+ if (ty->isDependentType())
return false;
+ if (Context.getLangOptions().ObjCAutoRefCount) {
+ switch (getObjCLifetime()) {
+ case Qualifiers::OCL_ExplicitNone:
+ return true;
+
+ case Qualifiers::OCL_Strong:
+ case Qualifiers::OCL_Weak:
+ case Qualifiers::OCL_Autoreleasing:
+ return false;
+
+ case Qualifiers::OCL_None:
+ if (ty->isObjCLifetimeType())
+ return false;
+ break;
+ }
+ }
+
// C++11 [basic.types]p9:
// Scalar types, POD classes, arrays of such types, and cv-qualified
// versions of these types are collectively called trivial types.
- const Type *BaseTy = getBaseElementTypeUnsafe();
+ const Type *BaseTy = ty->getBaseElementTypeUnsafe();
assert(BaseTy && "NULL element type");
// Return false for incomplete types after skipping any incomplete array
@@ -1392,7 +1490,8 @@
result->containsUnexpandedParameterPack(),
epi.ExtInfo),
NumArgs(numArgs), NumExceptions(epi.NumExceptions),
- ExceptionSpecType(epi.ExceptionSpecType)
+ ExceptionSpecType(epi.ExceptionSpecType),
+ HasAnyConsumedArgs(epi.ConsumedArguments != 0)
{
// Fill in the trailing argument array.
QualType *argSlot = reinterpret_cast<QualType*>(this+1);
@@ -1423,6 +1522,12 @@
Expr **noexSlot = reinterpret_cast<Expr**>(argSlot + numArgs);
*noexSlot = epi.NoexceptExpr;
}
+
+ if (epi.ConsumedArguments) {
+ bool *consumedArgs = const_cast<bool*>(getConsumedArgsBuffer());
+ for (unsigned i = 0; i != numArgs; ++i)
+ consumedArgs[i] = epi.ConsumedArguments[i];
+ }
}
FunctionProtoType::NoexceptResult
@@ -1461,6 +1566,24 @@
const QualType *ArgTys, unsigned NumArgs,
const ExtProtoInfo &epi,
const ASTContext &Context) {
+
+ // We have to be careful not to get ambiguous profile encodings.
+ // Note that valid type pointers are never ambiguous with anything else.
+ //
+ // The encoding grammar begins:
+ // type type* bool int bool
+ // If that final bool is true, then there is a section for the EH spec:
+ // bool type*
+ // This is followed by an optional "consumed argument" section of the
+ // same length as the first type sequence:
+ // bool*
+ // Finally, we have the ext info:
+ // int
+ //
+ // There is no ambiguity between the consumed arguments and an empty EH
+ // spec because of the leading 'bool' which unambiguously indicates
+ // whether the following bool is the EH spec or part of the arguments.
+
ID.AddPointer(Result.getAsOpaquePtr());
for (unsigned i = 0; i != NumArgs; ++i)
ID.AddPointer(ArgTys[i].getAsOpaquePtr());
@@ -1474,6 +1597,10 @@
} else if (epi.ExceptionSpecType == EST_ComputedNoexcept && epi.NoexceptExpr){
epi.NoexceptExpr->Profile(ID, Context, false);
}
+ if (epi.ConsumedArguments) {
+ for (unsigned i = 0; i != NumArgs; ++i)
+ ID.AddBoolean(epi.ConsumedArguments[i]);
+ }
epi.ExtInfo.Profile(ID);
}
@@ -1900,6 +2027,79 @@
CanonicalType->TypeBits.CacheValidAndVisibility = 0;
}
+Qualifiers::ObjCLifetime Type::getObjCARCImplicitLifetime() const {
+ if (isObjCARCImplicitlyUnretainedType())
+ return Qualifiers::OCL_ExplicitNone;
+ return Qualifiers::OCL_Strong;
+}
+
+bool Type::isObjCARCImplicitlyUnretainedType() const {
+ assert(isObjCLifetimeType() &&
+ "cannot query implicit lifetime for non-inferrable type");
+
+ const Type *canon = getCanonicalTypeInternal().getTypePtr();
+
+ // Walk down to the base type. We don't care about qualifiers for this.
+ while (const ArrayType *array = dyn_cast<ArrayType>(canon))
+ canon = array->getElementType().getTypePtr();
+
+ if (const ObjCObjectPointerType *opt
+ = dyn_cast<ObjCObjectPointerType>(canon)) {
+ // Class and Class<Protocol> don't require retension.
+ if (opt->getObjectType()->isObjCClass())
+ return true;
+ }
+
+ return false;
+}
+
+bool Type::isObjCNSObjectType() const {
+ if (const TypedefType *typedefType = dyn_cast<TypedefType>(this))
+ return typedefType->getDecl()->hasAttr<ObjCNSObjectAttr>();
+ return false;
+}
+bool Type::isObjCRetainableType() const {
+ return isObjCObjectPointerType() ||
+ isBlockPointerType() ||
+ isObjCNSObjectType();
+}
+bool Type::isObjCIndirectLifetimeType() const {
+ if (isObjCLifetimeType())
+ return true;
+ if (const PointerType *OPT = getAs<PointerType>())
+ return OPT->getPointeeType()->isObjCIndirectLifetimeType();
+ if (const ReferenceType *Ref = getAs<ReferenceType>())
+ return Ref->getPointeeType()->isObjCIndirectLifetimeType();
+ if (const MemberPointerType *MemPtr = getAs<MemberPointerType>())
+ return MemPtr->getPointeeType()->isObjCIndirectLifetimeType();
+ return false;
+}
+
+/// Returns true if objects of this type have lifetime semantics under
+/// ARC.
+bool Type::isObjCLifetimeType() const {
+ const Type *type = this;
+ while (const ArrayType *array = type->getAsArrayTypeUnsafe())
+ type = array->getElementType().getTypePtr();
+ return type->isObjCRetainableType();
+}
+
+/// \brief Determine whether the given type T is a "bridgable" Objective-C type,
+/// which is either an Objective-C object pointer type or an
+bool Type::isObjCARCBridgableType() const {
+ return isObjCObjectPointerType() || isBlockPointerType();
+}
+
+/// \brief Determine whether the given type T is a "bridgeable" C type.
+bool Type::isCARCBridgableType() const {
+ const PointerType *Pointer = getAs<PointerType>();
+ if (!Pointer)
+ return false;
+
+ QualType Pointee = Pointer->getPointeeType();
+ return Pointee->isVoidType() || Pointee->isRecordType();
+}
+
bool Type::hasSizedVLAType() const {
if (!isVariablyModifiedType()) return false;
@@ -1919,6 +2119,18 @@
}
QualType::DestructionKind QualType::isDestructedTypeImpl(QualType type) {
+ switch (type.getObjCLifetime()) {
+ case Qualifiers::OCL_None:
+ case Qualifiers::OCL_ExplicitNone:
+ case Qualifiers::OCL_Autoreleasing:
+ break;
+
+ case Qualifiers::OCL_Strong:
+ return DK_objc_strong_lifetime;
+ case Qualifiers::OCL_Weak:
+ return DK_objc_weak_lifetime;
+ }
+
/// Currently, the only destruction kind we recognize is C++ objects
/// with non-trivial destructors.
const CXXRecordDecl *record =
@@ -1928,3 +2140,24 @@
return DK_none;
}
+
+bool QualType::hasTrivialCopyAssignment(ASTContext &Context) const {
+ switch (getObjCLifetime()) {
+ case Qualifiers::OCL_None:
+ break;
+
+ case Qualifiers::OCL_ExplicitNone:
+ return true;
+
+ case Qualifiers::OCL_Autoreleasing:
+ case Qualifiers::OCL_Strong:
+ case Qualifiers::OCL_Weak:
+ return !Context.getLangOptions().ObjCAutoRefCount;
+ }
+
+ if (const CXXRecordDecl *Record
+ = getTypePtr()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl())
+ return Record->hasTrivialCopyAssignment();
+
+ return true;
+}
diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp
index 4519606..ccb4560 100644
--- a/lib/AST/TypePrinter.cpp
+++ b/lib/AST/TypePrinter.cpp
@@ -24,6 +24,23 @@
using namespace clang;
namespace {
+ /// \brief RAII object that enables printing of the ARC __strong lifetime
+ /// qualifier.
+ class IncludeStrongLifetimeRAII {
+ PrintingPolicy &Policy;
+ bool Old;
+
+ public:
+ explicit IncludeStrongLifetimeRAII(PrintingPolicy &Policy)
+ : Policy(Policy), Old(Policy.SuppressStrongLifetime) {
+ Policy.SuppressStrongLifetime = false;
+ }
+
+ ~IncludeStrongLifetimeRAII() {
+ Policy.SuppressStrongLifetime = Old;
+ }
+ };
+
class TypePrinter {
PrintingPolicy Policy;
@@ -78,7 +95,7 @@
// "int * const", printing "const int *" is different. Only do this when the
// type expands to a simple string.
bool CanPrefixQualifiers = false;
-
+ bool NeedARCStrongQualifier = false;
Type::TypeClass TC = T->getTypeClass();
if (const AutoType *AT = dyn_cast<AutoType>(T))
TC = AT->desugar()->getTypeClass();
@@ -114,15 +131,18 @@
T->isObjCQualifiedIdType() || T->isObjCQualifiedClassType();
break;
+ case Type::ConstantArray:
+ case Type::IncompleteArray:
+ case Type::VariableArray:
+ case Type::DependentSizedArray:
+ NeedARCStrongQualifier = true;
+ // Fall through
+
case Type::Pointer:
case Type::BlockPointer:
case Type::LValueReference:
case Type::RValueReference:
case Type::MemberPointer:
- case Type::ConstantArray:
- case Type::IncompleteArray:
- case Type::VariableArray:
- case Type::DependentSizedArray:
case Type::DependentSizedExtVector:
case Type::Vector:
case Type::ExtVector:
@@ -139,13 +159,20 @@
if (!CanPrefixQualifiers && !Quals.empty()) {
std::string qualsBuffer;
- Quals.getAsStringInternal(qualsBuffer, Policy);
-
- if (!buffer.empty()) {
- qualsBuffer += ' ';
- qualsBuffer += buffer;
+ if (NeedARCStrongQualifier) {
+ IncludeStrongLifetimeRAII Strong(Policy);
+ Quals.getAsStringInternal(qualsBuffer, Policy);
+ } else {
+ Quals.getAsStringInternal(qualsBuffer, Policy);
}
- std::swap(buffer, qualsBuffer);
+
+ if (!qualsBuffer.empty()) {
+ if (!buffer.empty()) {
+ qualsBuffer += ' ';
+ qualsBuffer += buffer;
+ }
+ std::swap(buffer, qualsBuffer);
+ }
}
switch (T->getTypeClass()) {
@@ -159,13 +186,20 @@
// If we're adding the qualifiers as a prefix, do it now.
if (CanPrefixQualifiers && !Quals.empty()) {
std::string qualsBuffer;
- Quals.getAsStringInternal(qualsBuffer, Policy);
-
- if (!buffer.empty()) {
- qualsBuffer += ' ';
- qualsBuffer += buffer;
+ if (NeedARCStrongQualifier) {
+ IncludeStrongLifetimeRAII Strong(Policy);
+ Quals.getAsStringInternal(qualsBuffer, Policy);
+ } else {
+ Quals.getAsStringInternal(qualsBuffer, Policy);
}
- std::swap(buffer, qualsBuffer);
+
+ if (!qualsBuffer.empty()) {
+ if (!buffer.empty()) {
+ qualsBuffer += ' ';
+ qualsBuffer += buffer;
+ }
+ std::swap(buffer, qualsBuffer);
+ }
}
}
@@ -192,6 +226,7 @@
if (isa<ArrayType>(T->getPointeeType()))
S = '(' + S + ')';
+ IncludeStrongLifetimeRAII Strong(Policy);
print(T->getPointeeType(), S);
}
@@ -209,6 +244,7 @@
if (isa<ArrayType>(T->getPointeeTypeAsWritten()))
S = '(' + S + ')';
+ IncludeStrongLifetimeRAII Strong(Policy);
print(T->getPointeeTypeAsWritten(), S);
}
@@ -221,6 +257,7 @@
if (isa<ArrayType>(T->getPointeeTypeAsWritten()))
S = '(' + S + ')';
+ IncludeStrongLifetimeRAII Strong(Policy);
print(T->getPointeeTypeAsWritten(), S);
}
@@ -236,6 +273,7 @@
if (isa<ArrayType>(T->getPointeeType()))
S = '(' + S + ')';
+ IncludeStrongLifetimeRAII Strong(Policy);
print(T->getPointeeType(), S);
}
@@ -245,12 +283,14 @@
S += llvm::utostr(T->getSize().getZExtValue());
S += ']';
+ IncludeStrongLifetimeRAII Strong(Policy);
print(T->getElementType(), S);
}
void TypePrinter::printIncompleteArray(const IncompleteArrayType *T,
std::string &S) {
S += "[]";
+ IncludeStrongLifetimeRAII Strong(Policy);
print(T->getElementType(), S);
}
@@ -276,6 +316,7 @@
}
S += ']';
+ IncludeStrongLifetimeRAII Strong(Policy);
print(T->getElementType(), S);
}
@@ -291,6 +332,7 @@
}
S += ']';
+ IncludeStrongLifetimeRAII Strong(Policy);
print(T->getElementType(), S);
}
@@ -518,6 +560,7 @@
if (!S.empty())
S = ' ' + S;
std::string Str;
+ IncludeStrongLifetimeRAII Strong(Policy);
print(T->getBaseType(), Str);
switch (T->getUTTKind()) {
@@ -552,6 +595,7 @@
Buffer += "<anonymous>";
} else if (ClassTemplateSpecializationDecl *Spec
= dyn_cast<ClassTemplateSpecializationDecl>(DC)) {
+ IncludeStrongLifetimeRAII Strong(Policy);
const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs();
std::string TemplateArgsStr
= TemplateSpecializationType::PrintTemplateArgumentList(
@@ -642,6 +686,7 @@
Args = TemplateArgs.data();
NumArgs = TemplateArgs.size();
}
+ IncludeStrongLifetimeRAII Strong(Policy);
Buffer += TemplateSpecializationType::PrintTemplateArgumentList(Args,
NumArgs,
Policy);
@@ -677,18 +722,21 @@
void TypePrinter::printSubstTemplateTypeParm(const SubstTemplateTypeParmType *T,
std::string &S) {
+ IncludeStrongLifetimeRAII Strong(Policy);
print(T->getReplacementType(), S);
}
void TypePrinter::printSubstTemplateTypeParmPack(
const SubstTemplateTypeParmPackType *T,
std::string &S) {
+ IncludeStrongLifetimeRAII Strong(Policy);
printTemplateTypeParm(T->getReplacedParameter(), S);
}
void TypePrinter::printTemplateSpecialization(
const TemplateSpecializationType *T,
std::string &S) {
+ IncludeStrongLifetimeRAII Strong(Policy);
std::string SpecString;
{
@@ -765,6 +813,7 @@
void TypePrinter::printDependentTemplateSpecialization(
const DependentTemplateSpecializationType *T, std::string &S) {
+ IncludeStrongLifetimeRAII Strong(Policy);
std::string MyString;
{
llvm::raw_string_ostream OS(MyString);
@@ -796,8 +845,9 @@
void TypePrinter::printAttributed(const AttributedType *T,
std::string &S) {
- // Prefer the macro forms of the GC qualifiers.
- if (T->getAttrKind() == AttributedType::attr_objc_gc)
+ // Prefer the macro forms of the GC and lifetime qualifiers.
+ if (T->getAttrKind() == AttributedType::attr_objc_gc ||
+ T->getAttrKind() == AttributedType::attr_objc_lifetime)
return print(T->getEquivalentType(), S);
print(T->getModifiedType(), S);
@@ -866,6 +916,18 @@
break;
}
+ case AttributedType::attr_objc_lifetime:
+ S += "objc_lifetime(";
+ switch (T->getEquivalentType().getObjCLifetime()) {
+ case Qualifiers::OCL_None: llvm_unreachable("no lifetime!"); break;
+ case Qualifiers::OCL_ExplicitNone: S += "none"; break;
+ case Qualifiers::OCL_Strong: S += "strong"; break;
+ case Qualifiers::OCL_Weak: S += "weak"; break;
+ case Qualifiers::OCL_Autoreleasing: S += "autoreleasing"; break;
+ }
+ S += ")";
+ break;
+
case AttributedType::attr_noreturn: S += "noreturn"; break;
case AttributedType::attr_cdecl: S += "cdecl"; break;
case AttributedType::attr_fastcall: S += "fastcall"; break;
@@ -1080,7 +1142,7 @@
// prefix a space if the string is non-empty. Will not append a final
// space.
void Qualifiers::getAsStringInternal(std::string &S,
- const PrintingPolicy&) const {
+ const PrintingPolicy& Policy) const {
AppendTypeQualList(S, getCVRQualifiers());
if (unsigned addrspace = getAddressSpace()) {
if (!S.empty()) S += ' ';
@@ -1095,6 +1157,23 @@
else
S += "__strong";
}
+ if (Qualifiers::ObjCLifetime lifetime = getObjCLifetime()) {
+ if (!S.empty() &&
+ !(lifetime == Qualifiers::OCL_Strong && Policy.SuppressStrongLifetime))
+ S += ' ';
+
+ switch (lifetime) {
+ case Qualifiers::OCL_None: llvm_unreachable("none but true");
+ case Qualifiers::OCL_ExplicitNone: S += "__unsafe_unretained"; break;
+ case Qualifiers::OCL_Strong:
+ if (!Policy.SuppressStrongLifetime)
+ S += "__strong";
+ break;
+
+ case Qualifiers::OCL_Weak: S += "__weak"; break;
+ case Qualifiers::OCL_Autoreleasing: S += "__autoreleasing"; break;
+ }
+ }
}
std::string QualType::getAsString(const Type *ty, Qualifiers qs) {