diff options
Diffstat (limited to 'clang/lib')
87 files changed, 7735 insertions, 967 deletions
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 4c66ed8d7e0..fac2eacc4f0 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -221,9 +221,9 @@ ASTContext::ASTContext(const LangOptions& LOpts, SourceManager &SM, 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 @@ ASTContext::getFunctionType(QualType ResultTy, 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 @@ ASTContext::getFunctionType(QualType ResultTy, 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 @@ CanQualType ASTContext::getCanonicalParamType(QualType T) const { return CanQualType::CreateUnsafe(Result); } - QualType ASTContext::getUnqualifiedArrayType(QualType type, Qualifiers &quals) { SplitQualType splitType = type.getSplitUnqualifiedType(); @@ -3725,11 +3730,7 @@ void ASTContext::setBlockDescriptorExtendedType(QualType T) { } 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 @@ CanQualType ASTContext::getFromTargetType(unsigned Type) const { // 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 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, 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 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, 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 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, // 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/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 08ac2a5be4d..c10e6c40d39 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -228,6 +228,11 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, 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 @@ NotASpecialMember:; // 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 @@ NotASpecialMember:; 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() { 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/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index e2c4f38ff9b..99eb0d386bd 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -474,8 +474,28 @@ void ObjCMethodDecl::createImplicitParams(ASTContext &Context, } 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/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 421770ea70f..19554a3baae 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -933,6 +933,11 @@ void DeclPrinter::VisitObjCPropertyDecl(ObjCPropertyDecl *PDecl) { 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/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 987213907e4..97f4ea6cba2 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1045,6 +1045,10 @@ const char *CastExpr::getCastKindName() const { 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 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, 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 @@ ObjCMessageExpr::ObjCMessageExpr(QualType T, /*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 @@ ObjCMessageExpr::ObjCMessageExpr(QualType T, 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 @@ ObjCMessageExpr::ObjCMessageExpr(QualType T, : 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 @@ ObjCInterfaceDecl *ObjCMessageExpr::getReceiverInterface() const { 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/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index d177cb5cbc9..1a1fa91a40a 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -162,6 +162,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { 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 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { 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/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 06c5645afb3..432ffee08de 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -282,6 +282,17 @@ public: 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 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_GetObjCProperty: case CK_LValueBitCast: case CK_UserDefinedConversion: + case CK_ObjCProduceObject: + case CK_ObjCConsumeObject: return false; case CK_LValueToRValue: @@ -2301,6 +2314,8 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { 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 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { case Expr::PackExpansionExprClass: case Expr::SubstNonTypeTemplateParmPackExprClass: case Expr::AsTypeExprClass: + case Expr::ObjCIndirectCopyRestoreExprClass: return ICEDiag(2, E->getLocStart()); case Expr::SizeOfPackExprClass: @@ -2995,7 +3011,8 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { 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/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index e81ec7e54b6..a77fe5f48ab 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/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 @@ void CXXNameMangler::mangleQualifiers(Qualifiers Quals) { 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 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) { 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 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) { 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/clang/lib/AST/ParentMap.cpp b/clang/lib/AST/ParentMap.cpp index eca351aec8d..b7b2005e9f8 100644 --- a/clang/lib/AST/ParentMap.cpp +++ b/clang/lib/AST/ParentMap.cpp @@ -66,6 +66,15 @@ Stmt *ParentMap::getParentIgnoreParenCasts(Stmt *S) const { 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/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 87588e45188..d6a67b13ef7 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -449,6 +449,12 @@ void StmtPrinter::VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *Node) { 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 @@ void StmtPrinter::VisitObjCMessageExpr(ObjCMessageExpr *Mess) { 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/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index b117cd9a525..c70c87a7240 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -220,6 +220,10 @@ void StmtProfiler::VisitObjCAtThrowStmt(ObjCAtThrowStmt *S) { VisitStmt(S); } +void StmtProfiler::VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { + VisitStmt(S); +} + void StmtProfiler::VisitExpr(Expr *S) { VisitStmt(S); } @@ -952,6 +956,17 @@ void StmtProfiler::VisitObjCIsaExpr(ObjCIsaExpr *S) { 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/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 938a686c2b5..080bca2198a 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -36,7 +36,10 @@ bool Qualifiers::isStrictSupersetOf(Qualifiers Other) const { (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 @@ bool Type::isIncompleteType() const { } } -/// 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 Type::isPODType() const { } } +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 @@ bool Type::isLiteralType() const { 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 @@ bool Type::isLiteralType() const { 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 @@ bool Type::isStandardLayoutType() const { // 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 @@ FunctionProtoType::FunctionProtoType(QualType result, const QualType *args, 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 @@ FunctionProtoType::FunctionProtoType(QualType result, const QualType *args, 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 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, 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 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, } 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 @@ void Type::ClearLinkageCache() { 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 @@ bool Type::hasSizedVLAType() const { } 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 @@ QualType::DestructionKind QualType::isDestructedTypeImpl(QualType type) { 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/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 4519606f6e8..ccb456018dd 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/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 @@ void TypePrinter::print(const Type *T, Qualifiers Quals, std::string &buffer) { // "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 @@ void TypePrinter::print(const Type *T, Qualifiers Quals, std::string &buffer) { 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 @@ void TypePrinter::print(const Type *T, Qualifiers Quals, std::string &buffer) { if (!CanPrefixQualifiers && !Quals.empty()) { std::string qualsBuffer; - Quals.getAsStringInternal(qualsBuffer, Policy); + if (NeedARCStrongQualifier) { + IncludeStrongLifetimeRAII Strong(Policy); + Quals.getAsStringInternal(qualsBuffer, Policy); + } else { + Quals.getAsStringInternal(qualsBuffer, Policy); + } - if (!buffer.empty()) { - qualsBuffer += ' '; - qualsBuffer += buffer; + if (!qualsBuffer.empty()) { + if (!buffer.empty()) { + qualsBuffer += ' '; + qualsBuffer += buffer; + } + std::swap(buffer, qualsBuffer); } - std::swap(buffer, qualsBuffer); } switch (T->getTypeClass()) { @@ -159,13 +186,20 @@ void TypePrinter::print(const Type *T, Qualifiers Quals, std::string &buffer) { // 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); + } + + if (!qualsBuffer.empty()) { + if (!buffer.empty()) { + qualsBuffer += ' '; + qualsBuffer += buffer; + } + std::swap(buffer, qualsBuffer); } - std::swap(buffer, qualsBuffer); } } @@ -192,6 +226,7 @@ void TypePrinter::printPointer(const PointerType *T, std::string &S) { if (isa<ArrayType>(T->getPointeeType())) S = '(' + S + ')'; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getPointeeType(), S); } @@ -209,6 +244,7 @@ void TypePrinter::printLValueReference(const LValueReferenceType *T, if (isa<ArrayType>(T->getPointeeTypeAsWritten())) S = '(' + S + ')'; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getPointeeTypeAsWritten(), S); } @@ -221,6 +257,7 @@ void TypePrinter::printRValueReference(const RValueReferenceType *T, if (isa<ArrayType>(T->getPointeeTypeAsWritten())) S = '(' + S + ')'; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getPointeeTypeAsWritten(), S); } @@ -236,6 +273,7 @@ void TypePrinter::printMemberPointer(const MemberPointerType *T, if (isa<ArrayType>(T->getPointeeType())) S = '(' + S + ')'; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getPointeeType(), S); } @@ -245,12 +283,14 @@ void TypePrinter::printConstantArray(const ConstantArrayType *T, 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 @@ void TypePrinter::printVariableArray(const VariableArrayType *T, } S += ']'; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getElementType(), S); } @@ -291,6 +332,7 @@ void TypePrinter::printDependentSizedArray(const DependentSizedArrayType *T, } S += ']'; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getElementType(), S); } @@ -518,6 +560,7 @@ void TypePrinter::printUnaryTransform(const UnaryTransformType *T, if (!S.empty()) S = ' ' + S; std::string Str; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getBaseType(), Str); switch (T->getUTTKind()) { @@ -552,6 +595,7 @@ void TypePrinter::AppendScope(DeclContext *DC, std::string &Buffer) { 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 @@ void TypePrinter::printTag(TagDecl *D, std::string &InnerString) { Args = TemplateArgs.data(); NumArgs = TemplateArgs.size(); } + IncludeStrongLifetimeRAII Strong(Policy); Buffer += TemplateSpecializationType::PrintTemplateArgumentList(Args, NumArgs, Policy); @@ -677,18 +722,21 @@ void TypePrinter::printTemplateTypeParm(const TemplateTypeParmType *T, 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::printDependentName(const DependentNameType *T, std::string &S) 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::printPackExpansion(const PackExpansionType *T, 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 @@ void TypePrinter::printAttributed(const AttributedType *T, 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 @@ std::string Qualifiers::getAsString() const { // 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 @@ void Qualifiers::getAsStringInternal(std::string &S, 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) { diff --git a/clang/lib/Analysis/ReachableCode.cpp b/clang/lib/Analysis/ReachableCode.cpp index 9ac456f53a6..c5b17fc77bb 100644 --- a/clang/lib/Analysis/ReachableCode.cpp +++ b/clang/lib/Analysis/ReachableCode.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/SmallVector.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" #include "clang/AST/StmtCXX.h" #include "clang/Analysis/Analyses/ReachableCode.h" #include "clang/Analysis/CFG.h" @@ -108,6 +109,11 @@ static SourceLocation GetUnreachableLoc(const CFGBlock &b, SourceRange &R1, case Stmt::CXXTryStmtClass: { return cast<CXXTryStmt>(S)->getHandler(0)->getCatchLoc(); } + case Expr::ObjCBridgedCastExprClass: { + const ObjCBridgedCastExpr *CSC = cast<ObjCBridgedCastExpr>(S); + R1 = CSC->getSubExpr()->getSourceRange(); + return CSC->getLParenLoc(); + } default: ; } R1 = S->getSourceRange(); diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp index 25dbcf0a6fe..b5b48cb2131 100644 --- a/clang/lib/Basic/DiagnosticIDs.cpp +++ b/clang/lib/Basic/DiagnosticIDs.cpp @@ -740,5 +740,10 @@ bool DiagnosticIDs::isUnrecoverable(unsigned DiagID) const { DiagID == diag::err_unavailable_message) return false; + // Currently we consider all ARC errors as recoverable. + if (getCategoryNumberForDiag(DiagID) == + diag::DiagCat_Automatic_Reference_Counting_Issue) + return false; + return true; } diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp index 4711faa1a51..109761c9dc9 100644 --- a/clang/lib/Basic/IdentifierTable.cpp +++ b/clang/lib/Basic/IdentifierTable.cpp @@ -17,6 +17,7 @@ #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/Support/raw_ostream.h" #include <cstdio> @@ -92,7 +93,8 @@ namespace { KEYBORLAND = 0x100, KEYOPENCL = 0x200, KEYC1X = 0x400, - KEYALL = 0x7ff + KEYARC = 0x800, + KEYALL = 0x0fff }; } @@ -120,7 +122,8 @@ static void AddKeyword(llvm::StringRef Keyword, else if (LangOpts.OpenCL && (Flags & KEYOPENCL)) AddResult = 2; else if (!LangOpts.CPlusPlus && (Flags & KEYNOCXX)) AddResult = 2; else if (LangOpts.C1X && (Flags & KEYC1X)) AddResult = 2; - + else if (LangOpts.ObjCAutoRefCount && (Flags & KEYARC)) AddResult = 2; + // Don't add this keyword if disabled in this language. if (AddResult == 0) return; diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp index dd167dca47b..541a1ed5954 100644 --- a/clang/lib/Basic/Targets.cpp +++ b/clang/lib/Basic/Targets.cpp @@ -84,15 +84,35 @@ static void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, Builder.defineMacro("__MACH__"); Builder.defineMacro("OBJC_NEW_PROPERTIES"); - // __weak is always defined, for use in blocks and with objc pointers. - Builder.defineMacro("__weak", "__attribute__((objc_gc(weak)))"); - - // Darwin defines __strong even in C mode (just to nothing). - if (!Opts.ObjC1 || Opts.getGCMode() == LangOptions::NonGC) - Builder.defineMacro("__strong", ""); - else - Builder.defineMacro("__strong", "__attribute__((objc_gc(strong)))"); + if (Opts.ObjCAutoRefCount) { + Builder.defineMacro("__weak", "__attribute__((objc_lifetime(weak)))"); + Builder.defineMacro("__strong", "__attribute__((objc_lifetime(strong)))"); + Builder.defineMacro("__autoreleasing", + "__attribute__((objc_lifetime(autoreleasing)))"); + Builder.defineMacro("__unsafe_unretained", + "__attribute__((objc_lifetime(none)))"); + } else { + // __weak is always defined, for use in blocks and with objc pointers. + Builder.defineMacro("__weak", "__attribute__((objc_gc(weak)))"); + // Darwin defines __strong even in C mode (just to nothing). + if (Opts.getGCMode() != LangOptions::NonGC) + Builder.defineMacro("__strong", "__attribute__((objc_gc(strong)))"); + else + Builder.defineMacro("__strong", ""); + + // __unsafe_unretained is defined to nothing in non-ARC mode. We even + // allow this in C, since one might have block pointers in structs that + // are used in pure C code and in Objective-C ARC. + Builder.defineMacro("__unsafe_unretained", ""); + + // The Objective-C bridged cast keywords are defined to nothing in non-ARC + // mode; then they become normal, C-style casts. + Builder.defineMacro("__bridge", ""); + Builder.defineMacro("__bridge_transfer", ""); + Builder.defineMacro("__bridge_retained", ""); + } + if (Opts.Static) Builder.defineMacro("__STATIC__"); else diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp index e5da703a61b..ac47034325b 100644 --- a/clang/lib/CodeGen/CGBlocks.cpp +++ b/clang/lib/CodeGen/CGBlocks.cpp @@ -347,13 +347,23 @@ static void computeBlockInfo(CodeGenModule &CGM, CGBlockInfo &info) { continue; } - // Block pointers require copy/dispose. - if (variable->getType()->isBlockPointerType()) { - info.NeedsCopyDispose = true; + // If we have a lifetime qualifier, honor it for capture purposes. + // That includes *not* copying it if it's __unsafe_unretained. + if (Qualifiers::ObjCLifetime lifetime + = variable->getType().getObjCLifetime()) { + switch (lifetime) { + case Qualifiers::OCL_None: llvm_unreachable("impossible"); + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + break; + + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Weak: + info.NeedsCopyDispose = true; + } - // So do Objective-C pointers. - } else if (variable->getType()->isObjCObjectPointerType() || - C.isObjCNSObjectType(variable->getType())) { + // Block pointers require copy/dispose. So do Objective-C pointers. + } else if (variable->getType()->isObjCRetainableType()) { info.NeedsCopyDispose = true; // So do types that require non-trivial copy construction. @@ -591,6 +601,11 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const BlockExpr *blockExpr) { // Otherwise, fake up a POD copy into the block field. } else { + // Fake up a new variable so that EmitScalarInit doesn't think + // we're referring to the variable in its own initializer. + ImplicitParamDecl blockFieldPseudoVar(/*DC*/ 0, SourceLocation(), + /*name*/ 0, type); + // We use one of these or the other depending on whether the // reference is nested. DeclRefExpr notNested(const_cast<VarDecl*>(variable), type, VK_LValue, @@ -603,15 +618,29 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const BlockExpr *blockExpr) { ImplicitCastExpr l2r(ImplicitCastExpr::OnStack, type, CK_LValueToRValue, declRef, VK_RValue); - EmitExprAsInit(&l2r, variable, blockField, + EmitExprAsInit(&l2r, &blockFieldPseudoVar, blockField, getContext().getDeclAlign(variable), /*captured by init*/ false); } // Push a destructor if necessary. The semantics for when this // actually gets run are really obscure. - if (!ci->isByRef() && CGM.getLangOptions().CPlusPlus) - PushDestructorCleanup(type, blockField); + if (!ci->isByRef()) { + switch (type.isDestructedType()) { + case QualType::DK_none: + break; + case QualType::DK_cxx_destructor: + PushDestructorCleanup(type, blockField); + break; + case QualType::DK_objc_strong_lifetime: + PushARCReleaseCleanup(getARCCleanupKind(), type, blockField, false); + break; + case QualType::DK_objc_weak_lifetime: + // __weak objects on the stack always get EH cleanups. + PushARCWeakReleaseCleanup(NormalAndEHCleanup, type, blockField); + break; + } + } } // Cast to the converted block-pointer type, which happens (somewhat @@ -1023,8 +1052,6 @@ CodeGenFunction::GenerateBlockFunction(GlobalDecl GD, - - llvm::Constant * CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { ASTContext &C = getContext(); @@ -1084,21 +1111,40 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { if (capture.isConstant()) continue; const Expr *copyExpr = ci->getCopyExpr(); - unsigned flags = 0; + BlockFieldFlags flags; + + bool isARCWeakCapture = false; if (copyExpr) { assert(!ci->isByRef()); // don't bother computing flags + } else if (ci->isByRef()) { flags = BLOCK_FIELD_IS_BYREF; - if (type.isObjCGCWeak()) flags |= BLOCK_FIELD_IS_WEAK; - } else if (type->isBlockPointerType()) { - flags = BLOCK_FIELD_IS_BLOCK; - } else if (type->isObjCObjectPointerType() || C.isObjCNSObjectType(type)) { + if (type.isObjCGCWeak()) + flags |= BLOCK_FIELD_IS_WEAK; + + } else if (type->isObjCRetainableType()) { flags = BLOCK_FIELD_IS_OBJECT; - } + if (type->isBlockPointerType()) + flags = BLOCK_FIELD_IS_BLOCK; - if (!copyExpr && !flags) continue; + // Special rules for ARC captures: + if (getLangOptions().ObjCAutoRefCount) { + Qualifiers qs = type.getQualifiers(); + + // Don't generate special copy logic for a captured object + // unless it's __strong or __weak. + if (!qs.hasStrongOrWeakObjCLifetime()) + continue; + + // Support __weak direct captures. + if (qs.getObjCLifetime() == Qualifiers::OCL_Weak) + isARCWeakCapture = true; + } + } else { + continue; + } unsigned index = capture.getIndex(); llvm::Value *srcField = Builder.CreateStructGEP(src, index); @@ -1107,12 +1153,14 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { // If there's an explicit copy expression, we do that. if (copyExpr) { EmitSynthesizedCXXCopyCtor(dstField, srcField, copyExpr); + } else if (isARCWeakCapture) { + EmitARCCopyWeak(dstField, srcField); } else { llvm::Value *srcValue = Builder.CreateLoad(srcField, "blockcopy.src"); srcValue = Builder.CreateBitCast(srcValue, VoidPtrTy); llvm::Value *dstAddr = Builder.CreateBitCast(dstField, VoidPtrTy); Builder.CreateCall3(CGM.getBlockObjectAssign(), dstAddr, srcValue, - llvm::ConstantInt::get(Int32Ty, flags)); + llvm::ConstantInt::get(Int32Ty, flags.getBitMask())); } } @@ -1176,20 +1224,37 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) { BlockFieldFlags flags; const CXXDestructorDecl *dtor = 0; + bool isARCWeakCapture = false; + if (ci->isByRef()) { flags = BLOCK_FIELD_IS_BYREF; - if (type.isObjCGCWeak()) flags |= BLOCK_FIELD_IS_WEAK; - } else if (type->isBlockPointerType()) { - flags = BLOCK_FIELD_IS_BLOCK; - } else if (type->isObjCObjectPointerType() || C.isObjCNSObjectType(type)) { + if (type.isObjCGCWeak()) + flags |= BLOCK_FIELD_IS_WEAK; + } else if (const CXXRecordDecl *record = type->getAsCXXRecordDecl()) { + if (record->hasTrivialDestructor()) + continue; + dtor = record->getDestructor(); + } else if (type->isObjCRetainableType()) { flags = BLOCK_FIELD_IS_OBJECT; - } else if (C.getLangOptions().CPlusPlus) { - if (const CXXRecordDecl *record = type->getAsCXXRecordDecl()) - if (!record->hasTrivialDestructor()) - dtor = record->getDestructor(); - } + if (type->isBlockPointerType()) + flags = BLOCK_FIELD_IS_BLOCK; + + // Special rules for ARC captures. + if (getLangOptions().ObjCAutoRefCount) { + Qualifiers qs = type.getQualifiers(); + + // Don't generate special dispose logic for a captured object + // unless it's __strong or __weak. + if (!qs.hasStrongOrWeakObjCLifetime()) + continue; - if (!dtor && flags.empty()) continue; + // Support __weak direct captures. + if (qs.getObjCLifetime() == Qualifiers::OCL_Weak) + isARCWeakCapture = true; + } + } else { + continue; + } unsigned index = capture.getIndex(); llvm::Value *srcField = Builder.CreateStructGEP(src, index); @@ -1198,6 +1263,10 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) { if (dtor) { PushDestructorCleanup(dtor, srcField); + // If this is a __weak capture, emit the release directly. + } else if (isARCWeakCapture) { + EmitARCDestroyWeak(srcField); + // Otherwise we call _Block_object_dispose. It wouldn't be too // hard to just emit this as a cleanup if we wanted to make sure // that things were done in reverse. @@ -1251,6 +1320,55 @@ public: } }; +/// Emits the copy/dispose helpers for an ARC __block __weak variable. +class ARCWeakByrefHelpers : public CodeGenModule::ByrefHelpers { +public: + ARCWeakByrefHelpers(CharUnits alignment) : ByrefHelpers(alignment) {} + + void emitCopy(CodeGenFunction &CGF, llvm::Value *destField, + llvm::Value *srcField) { + CGF.EmitARCMoveWeak(destField, srcField); + } + + void emitDispose(CodeGenFunction &CGF, llvm::Value *field) { + CGF.EmitARCDestroyWeak(field); + } + + void profileImpl(llvm::FoldingSetNodeID &id) const { + // 0 is distinguishable from all pointers and byref flags + id.AddInteger(0); + } +}; + +/// Emits the copy/dispose helpers for an ARC __block __strong variable +/// that's not of block-pointer type. +class ARCStrongByrefHelpers : public CodeGenModule::ByrefHelpers { +public: + ARCStrongByrefHelpers(CharUnits alignment) : ByrefHelpers(alignment) {} + + void emitCopy(CodeGenFunction &CGF, llvm::Value *destField, + llvm::Value *srcField) { + // Do a "move" by copying the value and then zeroing out the old + // variable. + + llvm::Value *value = CGF.Builder.CreateLoad(srcField); + llvm::Value *null = + llvm::ConstantPointerNull::get(cast<llvm::PointerType>(value->getType())); + CGF.Builder.CreateStore(value, destField); + CGF.Builder.CreateStore(null, srcField); + } + + void emitDispose(CodeGenFunction &CGF, llvm::Value *field) { + llvm::Value *value = CGF.Builder.CreateLoad(field); + CGF.EmitARCRelease(value, /*precise*/ false); + } + + void profileImpl(llvm::FoldingSetNodeID &id) const { + // 1 is distinguishable from all pointers and byref flags + id.AddInteger(1); + } +}; + /// Emits the copy/dispose helpers for a __block variable with a /// nontrivial copy constructor or destructor. class CXXByrefHelpers : public CodeGenModule::ByrefHelpers { @@ -1318,6 +1436,7 @@ generateByrefCopyHelper(CodeGenFunction &CGF, SC_Static, SC_None, false, true); + CGF.StartFunction(FD, R, Fn, FI, args, SourceLocation()); if (byrefInfo.needsCopy()) { @@ -1449,6 +1568,52 @@ CodeGenFunction::buildByrefHelpers(const llvm::StructType &byrefType, return ::buildByrefHelpers(CGM, byrefType, byrefInfo); } + // Otherwise, if we don't have a retainable type, there's nothing to do. + // that the runtime does extra copies. + if (!type->isObjCRetainableType()) return 0; + + Qualifiers qs = type.getQualifiers(); + + // If we have lifetime, that dominates. + if (Qualifiers::ObjCLifetime lifetime = qs.getObjCLifetime()) { + assert(getLangOptions().ObjCAutoRefCount); + + switch (lifetime) { + case Qualifiers::OCL_None: llvm_unreachable("impossible"); + + // These are just bits as far as the runtime is concerned. + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + return 0; + + // Tell the runtime that this is ARC __weak, called by the + // byref routines. + case Qualifiers::OCL_Weak: { + ARCWeakByrefHelpers byrefInfo(emission.Alignment); + return ::buildByrefHelpers(CGM, byrefType, byrefInfo); + } + + // ARC __strong __block variables need to be retained. + case Qualifiers::OCL_Strong: + // Block-pointers need to be _Block_copy'ed, so we let the + // runtime be in charge. But we can't use the code below + // because we don't want to set BYREF_CALLER, which will + // just make the runtime ignore us. + if (type->isBlockPointerType()) { + BlockFieldFlags flags = BLOCK_FIELD_IS_BLOCK; + ObjectByrefHelpers byrefInfo(emission.Alignment, flags); + return ::buildByrefHelpers(CGM, byrefType, byrefInfo); + + // Otherwise, we transfer ownership of the retain from the stack + // to the heap. + } else { + ARCStrongByrefHelpers byrefInfo(emission.Alignment); + return ::buildByrefHelpers(CGM, byrefType, byrefInfo); + } + } + llvm_unreachable("fell out of lifetime switch!"); + } + BlockFieldFlags flags; if (type->isBlockPointerType()) { flags |= BLOCK_FIELD_IS_BLOCK; @@ -1639,6 +1804,7 @@ namespace { CallBlockRelease(llvm::Value *Addr) : Addr(Addr) {} void Emit(CodeGenFunction &CGF, bool IsForEH) { + // Should we be passing FIELD_IS_WEAK here? CGF.BuildBlockRelease(Addr, BLOCK_FIELD_IS_BYREF); } }; diff --git a/clang/lib/CodeGen/CGBlocks.h b/clang/lib/CodeGen/CGBlocks.h index 9bd18e5fde7..35d61cfdd41 100644 --- a/clang/lib/CodeGen/CGBlocks.h +++ b/clang/lib/CodeGen/CGBlocks.h @@ -89,7 +89,7 @@ enum BlockFieldFlag_t { variable */ BLOCK_FIELD_IS_WEAK = 0x10, /* declared __weak, only used in byref copy helpers */ - + BLOCK_FIELD_IS_ARC = 0x40, /* field has ARC-specific semantics */ BLOCK_BYREF_CALLER = 128, /* called from __block (byref) copy/dispose support routines */ BLOCK_BYREF_CURRENT_MAX = 256 diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 05c11ceac62..712ec62bd40 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -25,6 +25,7 @@ #include "llvm/Attributes.h" #include "llvm/Support/CallSite.h" #include "llvm/Target/TargetData.h" +#include "llvm/InlineAsm.h" #include "llvm/Transforms/Utils/Local.h" using namespace clang; using namespace CodeGen; @@ -190,13 +191,15 @@ const CGFunctionInfo &CodeGenTypes::getFunctionInfo(const ObjCMethodDecl *MD) { e = MD->param_end(); i != e; ++i) { ArgTys.push_back(Context.getCanonicalParamType((*i)->getType())); } - return getFunctionInfo(GetReturnType(MD->getResultType()), - ArgTys, - FunctionType::ExtInfo( - /*NoReturn*/ false, - /*HasRegParm*/ false, - /*RegParm*/ 0, - getCallingConventionForDecl(MD))); + + FunctionType::ExtInfo einfo; + einfo = einfo.withCallingConv(getCallingConventionForDecl(MD)); + + if (getContext().getLangOptions().ObjCAutoRefCount && + MD->hasAttr<NSReturnsRetainedAttr>()) + einfo = einfo.withProducesResult(true); + + return getFunctionInfo(GetReturnType(MD->getResultType()), ArgTys, einfo); } const CGFunctionInfo &CodeGenTypes::getFunctionInfo(GlobalDecl GD) { @@ -262,7 +265,8 @@ const CGFunctionInfo &CodeGenTypes::getFunctionInfo(CanQualType ResTy, return *FI; // Construct the function info. - FI = new CGFunctionInfo(CC, Info.getNoReturn(), Info.getHasRegParm(), Info.getRegParm(), ResTy, + FI = new CGFunctionInfo(CC, Info.getNoReturn(), Info.getProducesResult(), + Info.getHasRegParm(), Info.getRegParm(), ResTy, ArgTys.data(), ArgTys.size()); FunctionInfos.InsertNode(FI, InsertPos); @@ -291,13 +295,15 @@ const CGFunctionInfo &CodeGenTypes::getFunctionInfo(CanQualType ResTy, } CGFunctionInfo::CGFunctionInfo(unsigned _CallingConvention, - bool _NoReturn, bool _HasRegParm, unsigned _RegParm, + bool _NoReturn, bool returnsRetained, + bool _HasRegParm, unsigned _RegParm, CanQualType ResTy, const CanQualType *ArgTys, unsigned NumArgTys) : CallingConvention(_CallingConvention), EffectiveCallingConvention(_CallingConvention), - NoReturn(_NoReturn), HasRegParm(_HasRegParm), RegParm(_RegParm) + NoReturn(_NoReturn), ReturnsRetained(returnsRetained), + HasRegParm(_HasRegParm), RegParm(_RegParm) { NumArgs = NumArgTys; @@ -1068,6 +1074,95 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, assert(AI == Fn->arg_end() && "Argument mismatch!"); } +/// Try to emit a fused autorelease of a return result. +static llvm::Value *tryEmitFusedAutoreleaseOfResult(CodeGenFunction &CGF, + llvm::Value *result) { + // We must be immediately followed the cast. + llvm::BasicBlock *BB = CGF.Builder.GetInsertBlock(); + if (BB->empty()) return 0; + if (&BB->back() != result) return 0; + + const llvm::Type *resultType = result->getType(); + + // result is in a BasicBlock and is therefore an Instruction. + llvm::Instruction *generator = cast<llvm::Instruction>(result); + + llvm::SmallVector<llvm::Instruction*,4> insnsToKill; + + // Look for: + // %generator = bitcast %type1* %generator2 to %type2* + while (llvm::BitCastInst *bitcast = dyn_cast<llvm::BitCastInst>(generator)) { + // We would have emitted this as a constant if the operand weren't + // an Instruction. + generator = cast<llvm::Instruction>(bitcast->getOperand(0)); + + // Require the generator to be immediately followed by the cast. + if (generator->getNextNode() != bitcast) + return 0; + + insnsToKill.push_back(bitcast); + } + + // Look for: + // %generator = call i8* @objc_retain(i8* %originalResult) + // or + // %generator = call i8* @objc_retainAutoreleasedReturnValue(i8* %originalResult) + llvm::CallInst *call = dyn_cast<llvm::CallInst>(generator); + if (!call) return 0; + + bool doRetainAutorelease; + + if (call->getCalledValue() == CGF.CGM.getARCEntrypoints().objc_retain) { + doRetainAutorelease = true; + } else if (call->getCalledValue() == CGF.CGM.getARCEntrypoints() + .objc_retainAutoreleasedReturnValue) { + doRetainAutorelease = false; + + // Look for an inline asm immediately preceding the call and kill it, too. + llvm::Instruction *prev = call->getPrevNode(); + if (llvm::CallInst *asmCall = dyn_cast_or_null<llvm::CallInst>(prev)) + if (asmCall->getCalledValue() + == CGF.CGM.getARCEntrypoints().retainAutoreleasedReturnValueMarker) + insnsToKill.push_back(prev); + } else { + return 0; + } + + result = call->getArgOperand(0); + insnsToKill.push_back(call); + + // Keep killing bitcasts, for sanity. Note that we no longer care + // about precise ordering as long as there's exactly one use. + while (llvm::BitCastInst *bitcast = dyn_cast<llvm::BitCastInst>(result)) { + if (!bitcast->hasOneUse()) break; + insnsToKill.push_back(bitcast); + result = bitcast->getOperand(0); + } + + // Delete all the unnecessary instructions, from latest to earliest. + for (llvm::SmallVectorImpl<llvm::Instruction*>::iterator + i = insnsToKill.begin(), e = insnsToKill.end(); i != e; ++i) + (*i)->eraseFromParent(); + + // Do the fused retain/autorelease if we were asked to. + if (doRetainAutorelease) + result = CGF.EmitARCRetainAutoreleaseReturnValue(result); + + // Cast back to the result type. + return CGF.Builder.CreateBitCast(result, resultType); +} + +/// Emit an ARC autorelease of the result of a function. +static llvm::Value *emitAutoreleaseOfResult(CodeGenFunction &CGF, + llvm::Value *result) { + // At -O0, try to emit a fused retain/autorelease. + if (CGF.shouldUseFusedARCCalls()) + if (llvm::Value *fused = tryEmitFusedAutoreleaseOfResult(CGF, result)) + return fused; + + return CGF.EmitARCAutoreleaseReturnValue(result); +} + void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI) { // Functions with no result always return void. if (ReturnValue == 0) { @@ -1135,6 +1230,16 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI) { RV = CreateCoercedLoad(V, RetAI.getCoerceToType(), *this); } + + // In ARC, end functions that return a retainable type with a call + // to objc_autoreleaseReturnValue. + if (AutoreleaseResult) { + assert(getLangOptions().ObjCAutoRefCount && + !FI.isReturnsRetained() && + RetTy->isObjCRetainableType()); + RV = emitAutoreleaseOfResult(*this, RV); + } + break; case ABIArgInfo::Ignore: @@ -1184,8 +1289,152 @@ void CodeGenFunction::EmitDelegateCallArg(CallArgList &args, return args.add(RValue::get(value), type); } +static bool isProvablyNull(llvm::Value *addr) { + return isa<llvm::ConstantPointerNull>(addr); +} + +static bool isProvablyNonNull(llvm::Value *addr) { + return isa<llvm::AllocaInst>(addr); +} + +/// Emit the actual writing-back of a writeback. +static void emitWriteback(CodeGenFunction &CGF, + const CallArgList::Writeback &writeback) { + llvm::Value *srcAddr = writeback.Address; + assert(!isProvablyNull(srcAddr) && + "shouldn't have writeback for provably null argument"); + + llvm::BasicBlock *contBB = 0; + + // If the argument wasn't provably non-null, we need to null check + // before doing the store. + bool provablyNonNull = isProvablyNonNull(srcAddr); + if (!provablyNonNull) { + llvm::BasicBlock *writebackBB = CGF.createBasicBlock("icr.writeback"); + contBB = CGF.createBasicBlock("icr.done"); + + llvm::Value *isNull = CGF.Builder.CreateIsNull(srcAddr, "icr.isnull"); + CGF.Builder.CreateCondBr(isNull, contBB, writebackBB); + CGF.EmitBlock(writebackBB); + } + + // Load the value to writeback. + llvm::Value *value = CGF.Builder.CreateLoad(writeback.Temporary); + + // Cast it back, in case we're writing an id to a Foo* or something. + value = CGF.Builder.CreateBitCast(value, + cast<llvm::PointerType>(srcAddr->getType())->getElementType(), + "icr.writeback-cast"); + + // Perform the writeback. + QualType srcAddrType = writeback.AddressType; + CGF.EmitStoreThroughLValue(RValue::get(value), + CGF.MakeAddrLValue(srcAddr, srcAddrType), + srcAddrType); + + // Jump to the continuation block. + if (!provablyNonNull) + CGF.EmitBlock(contBB); +} + +static void emitWritebacks(CodeGenFunction &CGF, + const CallArgList &args) { + for (CallArgList::writeback_iterator + i = args.writeback_begin(), e = args.writeback_end(); i != e; ++i) + emitWriteback(CGF, *i); +} + +/// Emit an argument that's being passed call-by-writeback. That is, +/// we are passing the address of +static void emitWritebackArg(CodeGenFunction &CGF, CallArgList &args, + const ObjCIndirectCopyRestoreExpr *CRE) { + llvm::Value *srcAddr = CGF.EmitScalarExpr(CRE->getSubExpr()); + + // The dest and src types don't necessarily match in LLVM terms + // because of the crazy ObjC compatibility rules. + + const llvm::PointerType *destType = + cast<llvm::PointerType>(CGF.ConvertType(CRE->getType())); + + // If the address is a constant null, just pass the appropriate null. + if (isProvablyNull(srcAddr)) { + args.add(RValue::get(llvm::ConstantPointerNull::get(destType)), + CRE->getType()); + return; + } + + QualType srcAddrType = + CRE->getSubExpr()->getType()->castAs<PointerType>()->getPointeeType(); + + // Create the temporary. + llvm::Value *temp = CGF.CreateTempAlloca(destType->getElementType(), + "icr.temp"); + + // Zero-initialize it if we're not doing a copy-initialization. + bool shouldCopy = CRE->shouldCopy(); + if (!shouldCopy) { + llvm::Value *null = + llvm::ConstantPointerNull::get( + cast<llvm::PointerType>(destType->getElementType())); + CGF.Builder.CreateStore(null, temp); + } + + llvm::BasicBlock *contBB = 0; + + // If the address is *not* known to be non-null, we need to switch. + llvm::Value *finalArgument; + + bool provablyNonNull = isProvablyNonNull(srcAddr); + if (provablyNonNull) { + finalArgument = temp; + } else { + llvm::Value *isNull = CGF.Builder.CreateIsNull(srcAddr, "icr.isnull"); + + finalArgument = CGF.Builder.CreateSelect(isNull, + llvm::ConstantPointerNull::get(destType), + temp, "icr.argument"); + + // If we need to copy, then the load has to be conditional, which + // means we need control flow. + if (shouldCopy) { + contBB = CGF.createBasicBlock("icr.cont"); + llvm::BasicBlock *copyBB = CGF.createBasicBlock("icr.copy"); + CGF.Builder.CreateCondBr(isNull, contBB, copyBB); + CGF.EmitBlock(copyBB); + } + } + + // Perform a copy if necessary. + if (shouldCopy) { + LValue srcLV = CGF.MakeAddrLValue(srcAddr, srcAddrType); + RValue srcRV = CGF.EmitLoadOfLValue(srcLV, srcAddrType); + assert(srcRV.isScalar()); + + llvm::Value *src = srcRV.getScalarVal(); + src = CGF.Builder.CreateBitCast(src, destType->getElementType(), + "icr.cast"); + + // Use an ordinary store, not a store-to-lvalue. + CGF.Builder.CreateStore(src, temp); + } + + // Finish the control flow if we needed it. + if (shouldCopy && !provablyNonNull) + CGF.EmitBlock(contBB); + + args.addWriteback(srcAddr, srcAddrType, temp); + args.add(RValue::get(finalArgument), CRE->getType()); +} + void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, QualType type) { + if (const ObjCIndirectCopyRestoreExpr *CRE + = dyn_cast<ObjCIndirectCopyRestoreExpr>(E)) { + assert(getContext().getLangOptions().ObjCAutoRefCount); + assert(getContext().hasSameType(E->getType(), type)); + return emitWritebackArg(*this, args, CRE); + } + if (type->isReferenceType()) return args.add(EmitReferenceBindingToExpr(E, /*InitializedDecl=*/0), type); @@ -1435,6 +1684,11 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, if (Builder.isNamePreserving() && !CI->getType()->isVoidTy()) CI->setName("call"); + // Emit any writebacks immediately. Arguably this should happen + // after any return-value munging. + if (CallArgs.hasWritebacks()) + emitWritebacks(*this, CallArgs); + switch (RetAI.getKind()) { case ABIArgInfo::Indirect: { unsigned Alignment = getContext().getTypeAlignInChars(RetTy).getQuantity(); diff --git a/clang/lib/CodeGen/CGCall.h b/clang/lib/CodeGen/CGCall.h index 160a62eab36..343b944bf6c 100644 --- a/clang/lib/CodeGen/CGCall.h +++ b/clang/lib/CodeGen/CGCall.h @@ -58,9 +58,44 @@ namespace CodeGen { class CallArgList : public llvm::SmallVector<CallArg, 16> { public: + struct Writeback { + /// The original argument. + llvm::Value *Address; + + /// The pointee type of the original argument. + QualType AddressType; + + /// The temporary alloca. + llvm::Value *Temporary; + }; + void add(RValue rvalue, QualType type, bool needscopy = false) { push_back(CallArg(rvalue, type, needscopy)); } + + void addFrom(const CallArgList &other) { + insert(end(), other.begin(), other.end()); + Writebacks.insert(Writebacks.end(), + other.Writebacks.begin(), other.Writebacks.end()); + } + + void addWriteback(llvm::Value *address, QualType addressType, + llvm::Value *temporary) { + Writeback writeback; + writeback.Address = address; + writeback.AddressType = addressType; + writeback.Temporary = temporary; + Writebacks.push_back(writeback); + } + + bool hasWritebacks() const { return !Writebacks.empty(); } + + typedef llvm::SmallVectorImpl<Writeback>::const_iterator writeback_iterator; + writeback_iterator writeback_begin() const { return Writebacks.begin(); } + writeback_iterator writeback_end() const { return Writebacks.end(); } + + private: + llvm::SmallVector<Writeback, 1> Writebacks; }; /// FunctionArgList - Type for representing both the decl and type @@ -88,6 +123,9 @@ namespace CodeGen { /// Whether this function is noreturn. bool NoReturn; + /// Whether this function is returns-retained. + bool ReturnsRetained; + unsigned NumArgs; ArgInfo *Args; @@ -100,7 +138,8 @@ namespace CodeGen { typedef ArgInfo *arg_iterator; CGFunctionInfo(unsigned CallingConvention, bool NoReturn, - bool HasRegParm, unsigned RegParm, CanQualType ResTy, + bool ReturnsRetained, bool HasRegParm, unsigned RegParm, + CanQualType ResTy, const CanQualType *ArgTys, unsigned NumArgTys); ~CGFunctionInfo() { delete[] Args; } @@ -113,6 +152,10 @@ namespace CodeGen { bool isNoReturn() const { return NoReturn; } + /// In ARR, whether this function retains its return value. This + /// is not always reliable for call sites. + bool isReturnsRetained() const { return ReturnsRetained; } + /// getCallingConvention - Return the user specified calling /// convention. unsigned getCallingConvention() const { return CallingConvention; } @@ -137,6 +180,7 @@ namespace CodeGen { void Profile(llvm::FoldingSetNodeID &ID) { ID.AddInteger(getCallingConvention()); ID.AddBoolean(NoReturn); + ID.AddBoolean(ReturnsRetained); ID.AddBoolean(HasRegParm); ID.AddInteger(RegParm); getReturnType().Profile(ID); @@ -151,6 +195,7 @@ namespace CodeGen { Iterator end) { ID.AddInteger(Info.getCC()); ID.AddBoolean(Info.getNoReturn()); + ID.AddBoolean(Info.getProducesResult()); ID.AddBoolean(Info.getHasRegParm()); ID.AddInteger(Info.getRegParm()); ResTy.Profile(ID); diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 5725d80b7d0..066f0d5c7d7 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -398,7 +398,8 @@ static void EmitBaseInitializer(CodeGenFunction &CGF, BaseClassDecl, isBaseVirtual); - AggValueSlot AggSlot = AggValueSlot::forAddr(V, false, /*Lifetime*/ true); + AggValueSlot AggSlot = AggValueSlot::forAddr(V, Qualifiers(), + /*Lifetime*/ true); CGF.EmitAggExpr(BaseInit->getInit(), AggSlot); @@ -428,10 +429,20 @@ static void EmitAggMemberInitializer(CodeGenFunction &CGF, CGF.Builder.CreateStore(Next, ArrayIndexVar); } - AggValueSlot Slot = AggValueSlot::forAddr(Dest, LHS.isVolatileQualified(), - /*Lifetime*/ true); - - CGF.EmitAggExpr(MemberInit->getInit(), Slot); + if (!CGF.hasAggregateLLVMType(T)) { + CGF.EmitScalarInit(MemberInit->getInit(), 0, Dest, false, + LHS.isVolatileQualified(), + CGF.getContext().getTypeAlign(T), + T); + } else if (T->isAnyComplexType()) { + CGF.EmitComplexExprIntoAddr(MemberInit->getInit(), Dest, + LHS.isVolatileQualified()); + } else { + AggValueSlot Slot = AggValueSlot::forAddr(Dest, LHS.getQuals(), + /*Lifetime*/ true); + + CGF.EmitAggExpr(MemberInit->getInit(), Slot); + } return; } @@ -540,15 +551,16 @@ static void EmitMemberInitializer(CodeGenFunction &CGF, // FIXME: If there's no initializer and the CXXCtorInitializer // was implicitly generated, we shouldn't be zeroing memory. - RValue RHS; - if (FieldType->isReferenceType()) { - RHS = CGF.EmitReferenceBindingToExpr(MemberInit->getInit(), Field); - CGF.EmitStoreThroughLValue(RHS, LHS, FieldType); - } else if (FieldType->isArrayType() && !MemberInit->getInit()) { + if (FieldType->isArrayType() && !MemberInit->getInit()) { CGF.EmitNullInitialization(LHS.getAddress(), Field->getType()); } else if (!CGF.hasAggregateLLVMType(Field->getType())) { - RHS = RValue::get(CGF.EmitScalarExpr(MemberInit->getInit())); - CGF.EmitStoreThroughLValue(RHS, LHS, FieldType); + if (LHS.isSimple()) { + CGF.EmitExprAsInit(MemberInit->getInit(), Field, LHS.getAddress(), + CGF.getContext().getDeclAlign(Field), false); + } else { + RValue RHS = RValue::get(CGF.EmitScalarExpr(MemberInit->getInit())); + CGF.EmitStoreThroughLValue(RHS, LHS, FieldType); + } } else if (MemberInit->getInit()->getType()->isAnyComplexType()) { CGF.EmitComplexExprIntoAddr(MemberInit->getInit(), LHS.getAddress(), LHS.isVolatileQualified()); @@ -576,11 +588,11 @@ static void EmitMemberInitializer(CodeGenFunction &CGF, llvm::Value *Zero = llvm::Constant::getNullValue(SizeTy); CGF.Builder.CreateStore(Zero, ArrayIndexVar); - // If we are copying an array of scalars or classes with trivial copy + // If we are copying an array of PODs or classes with trivial copy // constructors, perform a single aggregate copy. - const RecordType *Record = BaseElementTy->getAs<RecordType>(); - if (!Record || - cast<CXXRecordDecl>(Record->getDecl())->hasTrivialCopyConstructor()) { + const CXXRecordDecl *Record = BaseElementTy->getAsCXXRecordDecl(); + if (BaseElementTy.isPODType(CGF.getContext()) || + (Record && Record->hasTrivialCopyConstructor())) { // Find the source pointer. We knows it's the last argument because // we know we're in a copy constructor. unsigned SrcArgIndex = Args.size() - 1; @@ -925,12 +937,8 @@ namespace { CallArrayFieldDtor(const FieldDecl *Field) : Field(Field) {} void Emit(CodeGenFunction &CGF, bool IsForEH) { - QualType FieldType = Field->getType(); - const ConstantArrayType *Array = - CGF.getContext().getAsConstantArrayType(FieldType); - - QualType BaseType = - CGF.getContext().getBaseElementType(Array->getElementType()); + QualType FieldType = Field->getType(); + QualType BaseType = CGF.getContext().getBaseElementType(FieldType); const CXXRecordDecl *FieldClassDecl = BaseType->getAsCXXRecordDecl(); llvm::Value *ThisPtr = CGF.LoadCXXThis(); @@ -938,9 +946,12 @@ namespace { // FIXME: Qualifiers? /*CVRQualifiers=*/0); - const llvm::Type *BasePtr = CGF.ConvertType(BaseType)->getPointerTo(); - llvm::Value *BaseAddrPtr = - CGF.Builder.CreateBitCast(LHS.getAddress(), BasePtr); + const llvm::Type *BasePtr + = CGF.ConvertType(BaseType)->getPointerTo(); + llvm::Value *BaseAddrPtr + = CGF.Builder.CreateBitCast(LHS.getAddress(), BasePtr); + const ConstantArrayType *Array + = CGF.getContext().getAsConstantArrayType(FieldType); CGF.EmitCXXAggrDestructorCall(FieldClassDecl->getDestructor(), Array, BaseAddrPtr); } @@ -1042,19 +1053,26 @@ void CodeGenFunction::EnterDtorCleanups(const CXXDestructorDecl *DD, getContext().getAsConstantArrayType(FieldType); if (Array) FieldType = getContext().getBaseElementType(Array->getElementType()); - - const RecordType *RT = FieldType->getAs<RecordType>(); - if (!RT) + + switch (FieldType.isDestructedType()) { + case QualType::DK_none: continue; - - CXXRecordDecl *FieldClassDecl = cast<CXXRecordDecl>(RT->getDecl()); - if (FieldClassDecl->hasTrivialDestructor()) - continue; + + case QualType::DK_cxx_destructor: + if (Array) + EHStack.pushCleanup<CallArrayFieldDtor>(NormalAndEHCleanup, Field); + else + EHStack.pushCleanup<CallFieldDtor>(NormalAndEHCleanup, Field); + break; + + case QualType::DK_objc_strong_lifetime: + PushARCFieldReleaseCleanup(getARCCleanupKind(), Field); + break; - if (Array) - EHStack.pushCleanup<CallArrayFieldDtor>(NormalAndEHCleanup, Field); - else - EHStack.pushCleanup<CallFieldDtor>(NormalAndEHCleanup, Field); + case QualType::DK_objc_weak_lifetime: + PushARCFieldWeakReleaseCleanup(getARCCleanupKind(), Field); + break; + } } } @@ -1384,7 +1402,8 @@ CodeGenFunction::EmitDelegatingCXXConstructorCall(const CXXConstructorDecl *Ctor llvm::Value *ThisPtr = LoadCXXThis(); - AggValueSlot AggSlot = AggValueSlot::forAddr(ThisPtr, false, /*Lifetime*/ true); + AggValueSlot AggSlot = + AggValueSlot::forAddr(ThisPtr, Qualifiers(), /*Lifetime*/ true); EmitAggExpr(Ctor->init_begin()[0]->getInit(), AggSlot); diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 98d30db647d..88cc5be4ded 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -389,6 +389,7 @@ llvm::DIType CGDebugInfo::CreateQualifiedType(QualType Ty, llvm::DIFile Unit) { // Ignore these qualifiers for now. Qc.removeObjCGCAttr(); Qc.removeAddressSpace(); + Qc.removeObjCLifetime(); // We will create one Derived type for one qualifier and recurse to handle any // additional ones. diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 33c4ada68f8..14e999f589c 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -353,9 +353,7 @@ namespace { if (NRVO) CGF.EmitBlock(SkipDtorBB); } }; -} -namespace { struct CallStackRestore : EHScopeStack::Cleanup { llvm::Value *Stack; CallStackRestore(llvm::Value *Stack) : Stack(Stack) {} @@ -400,6 +398,164 @@ namespace { }; } +/// EmitAutoVarWithLifetime - Does the setup required for an automatic +/// variable with lifetime. +static void EmitAutoVarWithLifetime(CodeGenFunction &CGF, const VarDecl &var, + llvm::Value *addr, + Qualifiers::ObjCLifetime lifetime) { + switch (lifetime) { + case Qualifiers::OCL_None: + llvm_unreachable("present but none"); + + case Qualifiers::OCL_ExplicitNone: + // nothing to do + break; + + case Qualifiers::OCL_Strong: { + CGF.PushARCReleaseCleanup(CGF.getARCCleanupKind(), + var.getType(), addr, + var.hasAttr<ObjCPreciseLifetimeAttr>()); + break; + } + case Qualifiers::OCL_Autoreleasing: + // nothing to do + break; + + case Qualifiers::OCL_Weak: + // __weak objects always get EH cleanups; otherwise, exceptions + // could cause really nasty crashes instead of mere leaks. + CGF.PushARCWeakReleaseCleanup(NormalAndEHCleanup, var.getType(), addr); + break; + } +} + +static bool isAccessedBy(const VarDecl &var, const Stmt *s) { + if (const Expr *e = dyn_cast<Expr>(s)) { + // Skip the most common kinds of expressions that make + // hierarchy-walking expensive. + s = e = e->IgnoreParenCasts(); + + if (const DeclRefExpr *ref = dyn_cast<DeclRefExpr>(e)) + return (ref->getDecl() == &var); + } + + for (Stmt::const_child_range children = s->children(); children; ++children) + if (isAccessedBy(var, *children)) + return true; + + return false; +} + +static bool isAccessedBy(const ValueDecl *decl, const Expr *e) { + if (!decl) return false; + if (!isa<VarDecl>(decl)) return false; + const VarDecl *var = cast<VarDecl>(decl); + return isAccessedBy(*var, e); +} + +void CodeGenFunction::EmitScalarInit(const Expr *init, + const ValueDecl *D, + llvm::Value *addr, bool capturedByInit, + bool isVolatile, unsigned alignment, + QualType type) { + Qualifiers::ObjCLifetime lifetime = type.getQualifiers().getObjCLifetime(); + if (!lifetime) { + llvm::Value *value = EmitScalarExpr(init); + if (capturedByInit) addr = BuildBlockByrefAddress(addr, cast<VarDecl>(D)); + EmitStoreOfScalar(value, addr, isVolatile, alignment, type); + return; + } + + // If we're emitting a value with lifetime, we have to do the + // initialization *before* we leave the cleanup scopes. + CodeGenFunction::RunCleanupsScope Scope(*this); + if (const ExprWithCleanups *ewc = dyn_cast<ExprWithCleanups>(init)) + init = ewc->getSubExpr(); + + // We have to maintain the illusion that the variable is + // zero-initialized. If the variable might be accessed in its + // initializer, zero-initialize before running the initializer, then + // actually perform the initialization with an assign. + bool accessedByInit = false; + if (lifetime != Qualifiers::OCL_ExplicitNone) + accessedByInit = isAccessedBy(D, init); + if (accessedByInit) { + // Drill down to the __block object if necessary. + llvm::Value *tempAddr = addr; + if (capturedByInit) { + // We can use a simple GEP for this because it can't have been + // moved yet. + tempAddr = Builder.CreateStructGEP(tempAddr, + getByRefValueLLVMField(cast<VarDecl>(D))); + } + + const llvm::PointerType *ty = cast<llvm::PointerType>(tempAddr->getType()); + ty = cast<llvm::PointerType>(ty->getElementType()); + + llvm::Value *zero = llvm::ConstantPointerNull::get(ty); + + // If __weak, we want to use a barrier under certain conditions. + if (lifetime == Qualifiers::OCL_Weak) + EmitARCInitWeak(tempAddr, zero); + + // Otherwise just do a simple store. + else + EmitStoreOfScalar(zero, tempAddr, isVolatile, alignment, type); + } + + // Emit the initializer. + llvm::Value *value = 0; + + switch (lifetime) { + case Qualifiers::OCL_None: + llvm_unreachable("present but none"); + + case Qualifiers::OCL_ExplicitNone: + // nothing to do + value = EmitScalarExpr(init); + break; + + case Qualifiers::OCL_Strong: { + value = EmitARCRetainScalarExpr(init); + break; + } + + case Qualifiers::OCL_Weak: { + // No way to optimize a producing initializer into this. It's not + // worth optimizing for, because the value will immediately + // disappear in the common case. + value = EmitScalarExpr(init); + + if (capturedByInit) addr = BuildBlockByrefAddress(addr, cast<VarDecl>(D)); + if (accessedByInit) + EmitARCStoreWeak(addr, value, /*ignored*/ true); + else + EmitARCInitWeak(addr, value); + return; + } + + case Qualifiers::OCL_Autoreleasing: + value = EmitARCRetainAutoreleaseScalarExpr(init); + break; + } + + if (capturedByInit) addr = BuildBlockByrefAddress(addr, cast<VarDecl>(D)); + + llvm::MDNode *tbaa = CGM.getTBAAInfo(type); + + // If the variable might have been accessed by its initializer, we + // might have to initialize with a barrier. We have to do this for + // both __weak and __strong, but __weak got filtered out above. + if (accessedByInit && lifetime == Qualifiers::OCL_Strong) { + llvm::Value *oldValue + = EmitLoadOfScalar(addr, isVolatile, alignment, type, tbaa); + EmitStoreOfScalar(value, addr, isVolatile, alignment, type, tbaa); + EmitARCRelease(oldValue, /*precise*/ false); + return; + } + + EmitStoreOfScalar(value, addr, isVolatile, alignment, type, tbaa); +} /// canEmitInitWithFewStoresAfterMemset - Decide whether we can emit the /// non-zero parts of the specified initializer with equal or fewer than @@ -521,7 +677,9 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { // arrays as long as the initialization is trivial (e.g. if they // have a non-trivial destructor, but not a non-trivial constructor). if (D.getInit() && - (Ty->isArrayType() || Ty->isRecordType()) && Ty->isPODType() && + (Ty->isArrayType() || Ty->isRecordType()) && + (Ty.isPODType(getContext()) || + getContext().getBaseElementType(Ty)->isObjCObjectPointerType()) && D.getInit()->isConstantInitializer(getContext(), false)) { // If the variable's a const type, and it's neither an NRVO @@ -765,29 +923,30 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { /// \param capturedByInit true if the variable is a __block variable /// whose address is potentially changed by the initializer void CodeGenFunction::EmitExprAsInit(const Expr *init, - const VarDecl *var, + const ValueDecl *D, llvm::Value *loc, CharUnits alignment, bool capturedByInit) { - QualType type = var->getType(); + QualType type = D->getType(); bool isVolatile = type.isVolatileQualified(); if (type->isReferenceType()) { - RValue RV = EmitReferenceBindingToExpr(init, var); - if (capturedByInit) loc = BuildBlockByrefAddress(loc, var); + RValue RV = EmitReferenceBindingToExpr(init, D); + if (capturedByInit) + loc = BuildBlockByrefAddress(loc, cast<VarDecl>(D)); EmitStoreOfScalar(RV.getScalarVal(), loc, false, alignment.getQuantity(), type); } else if (!hasAggregateLLVMType(type)) { - llvm::Value *V = EmitScalarExpr(init); - if (capturedByInit) loc = BuildBlockByrefAddress(loc, var); - EmitStoreOfScalar(V, loc, isVolatile, alignment.getQuantity(), type); + EmitScalarInit(init, D, loc, capturedByInit, isVolatile, + alignment.getQuantity(), type); } else if (type->isAnyComplexType()) { ComplexPairTy complex = EmitComplexExpr(init); - if (capturedByInit) loc = BuildBlockByrefAddress(loc, var); + if (capturedByInit) loc = BuildBlockByrefAddress(loc, cast<VarDecl>(D)); StoreComplexToAddr(complex, loc, isVolatile); } else { // TODO: how can we delay here if D is captured by its initializer? - EmitAggExpr(init, AggValueSlot::forAddr(loc, isVolatile, true, false)); + EmitAggExpr(init, AggValueSlot::forAddr(loc, type.getQualifiers(), true, + false)); } } @@ -799,7 +958,7 @@ void CodeGenFunction::EmitAutoVarCleanups(const AutoVarEmission &emission) { const VarDecl &D = *emission.Variable; - // Handle C++ destruction of variables. + // Handle C++ or ARC destruction of variables. if (getLangOptions().CPlusPlus) { QualType type = D.getType(); QualType baseType = getContext().getBaseElementType(type); @@ -830,6 +989,12 @@ void CodeGenFunction::EmitAutoVarCleanups(const AutoVarEmission &emission) { } } + if (Qualifiers::ObjCLifetime lifetime + = D.getType().getQualifiers().getObjCLifetime()) { + llvm::Value *loc = emission.getObjectAddress(*this); + EmitAutoVarWithLifetime(*this, D, loc, lifetime); + } + // Handle the cleanup attribute. if (const CleanupAttr *CA = D.getAttr<CleanupAttr>()) { const FunctionDecl *FD = CA->getFunctionDecl(); @@ -847,6 +1012,22 @@ void CodeGenFunction::EmitAutoVarCleanups(const AutoVarEmission &emission) { enterByrefCleanup(emission); } +namespace { + /// A cleanup to perform a release of an object at the end of a + /// function. This is used to balance out the incoming +1 of a + /// ns_consumed argument when we can't reasonably do that just by + /// not doing the initial retain for a __block argument. + struct ConsumeARCParameter : EHScopeStack::Cleanup { + ConsumeARCParameter(llvm::Value *param) : Param(param) {} + + llvm::Value *Param; + + void Emit(CodeGenFunction &CGF, bool IsForEH) { + CGF.EmitARCRelease(Param, /*precise*/ false); + } + }; +} + /// Emit an alloca (or GlobalValue depending on target) /// for the specified parameter and set up LocalDeclMap. void CodeGenFunction::EmitParmDecl(const VarDecl &D, llvm::Value *Arg, @@ -883,10 +1064,53 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, llvm::Value *Arg, // Otherwise, create a temporary to hold the value. DeclPtr = CreateMemTemp(Ty, D.getName() + ".addr"); + bool doStore = true; + + Qualifiers qs = Ty.getQualifiers(); + + if (Qualifiers::ObjCLifetime lt = qs.getObjCLifetime()) { + // We honor __attribute__((ns_consumed)) for types with lifetime. + // For __strong, it's handled by just skipping the initial retain; + // otherwise we have to balance out the initial +1 with an extra + // cleanup to do the release at the end of the function. + bool isConsumed = D.hasAttr<NSConsumedAttr>(); + + // 'self' is always formally __strong, but if this is not an + // init method then we don't want to retain it. + if (lt == Qualifiers::OCL_Strong && qs.hasConst() && + isa<ImplicitParamDecl>(D)) { + const ObjCMethodDecl *method = cast<ObjCMethodDecl>(CurCodeDecl); + assert(&D == method->getSelfDecl()); + assert(method->getMethodFamily() != OMF_init); + lt = Qualifiers::OCL_ExplicitNone; + } + + if (lt == Qualifiers::OCL_Strong) { + if (!isConsumed) + // Don't use objc_retainBlock for block pointers, because we + // don't want to Block_copy something just because we got it + // as a parameter. + Arg = EmitARCRetainNonBlock(Arg); + } else { + // Push the cleanup for a consumed parameter. + if (isConsumed) + EHStack.pushCleanup<ConsumeARCParameter>(getARCCleanupKind(), Arg); + + if (lt == Qualifiers::OCL_Weak) { + EmitARCInitWeak(DeclPtr, Arg); + doStore = false; // The weak init is a store, no need to do two + } + } + + // Enter the cleanup scope. + EmitAutoVarWithLifetime(*this, D, DeclPtr, lt); + } + // Store the initial value into the alloca. - EmitStoreOfScalar(Arg, DeclPtr, Ty.isVolatileQualified(), - getContext().getDeclAlign(&D).getQuantity(), Ty, - CGM.getTBAAInfo(Ty)); + if (doStore) + EmitStoreOfScalar(Arg, DeclPtr, Ty.isVolatileQualified(), + getContext().getDeclAlign(&D).getQuantity(), Ty, + CGM.getTBAAInfo(Ty)); } llvm::Value *&DMEntry = LocalDeclMap[&D]; diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp index 178badd44d8..08e8e0c48e3 100644 --- a/clang/lib/CodeGen/CGDeclCXX.cpp +++ b/clang/lib/CodeGen/CGDeclCXX.cpp @@ -34,20 +34,22 @@ static void EmitDeclInit(CodeGenFunction &CGF, const VarDecl &D, unsigned Alignment = Context.getDeclAlign(&D).getQuantity(); if (!CGF.hasAggregateLLVMType(T)) { - llvm::Value *V = CGF.EmitScalarExpr(Init); CodeGenModule &CGM = CGF.CGM; Qualifiers::GC GCAttr = CGM.getContext().getObjCGCAttrKind(T); if (GCAttr == Qualifiers::Strong) - CGM.getObjCRuntime().EmitObjCGlobalAssign(CGF, V, DeclPtr, - D.isThreadSpecified()); + CGM.getObjCRuntime().EmitObjCGlobalAssign(CGF, CGF.EmitScalarExpr(Init), + DeclPtr, D.isThreadSpecified()); else if (GCAttr == Qualifiers::Weak) - CGM.getObjCRuntime().EmitObjCWeakAssign(CGF, V, DeclPtr); + CGM.getObjCRuntime().EmitObjCWeakAssign(CGF, CGF.EmitScalarExpr(Init), + DeclPtr); else - CGF.EmitStoreOfScalar(V, DeclPtr, isVolatile, Alignment, T); + CGF.EmitScalarInit(Init, &D, DeclPtr, false, isVolatile, Alignment, + D.getType()); } else if (T->isAnyComplexType()) { CGF.EmitComplexExprIntoAddr(Init, DeclPtr, isVolatile); } else { - CGF.EmitAggExpr(Init, AggValueSlot::forAddr(DeclPtr, isVolatile, true)); + CGF.EmitAggExpr(Init, AggValueSlot::forAddr(DeclPtr, T.getQualifiers(), + true)); } } @@ -291,10 +293,21 @@ void CodeGenFunction::GenerateCXXGlobalInitFunc(llvm::Function *Fn, getTypes().getNullaryFunctionInfo(), FunctionArgList(), SourceLocation()); + RunCleanupsScope Scope(*this); + + // When building in Objective-C++ ARC mode, create an autorelease pool + // around the global initializers. + if (getLangOptions().ObjCAutoRefCount && getLangOptions().CPlusPlus) { + llvm::Value *token = EmitObjCAutoreleasePoolPush(); + EmitObjCAutoreleasePoolCleanup(token); + } + for (unsigned i = 0; i != NumDecls; ++i) if (Decls[i]) - Builder.CreateCall(Decls[i]); + Builder.CreateCall(Decls[i]); + Scope.ForceCleanup(); + FinishFunction(); } diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index e8ad6da2f98..ce57d02bcd3 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -354,7 +354,8 @@ static void EmitAnyExprToExn(CodeGenFunction &CGF, const Expr *e, // evaluated but before the exception is caught. But the best way // to handle that is to teach EmitAggExpr to do the final copy // differently if it can't be elided. - CGF.EmitAnyExprToMem(e, typedAddr, /*Volatile*/ false, /*IsInit*/ true); + CGF.EmitAnyExprToMem(e, typedAddr, e->getType().getQualifiers(), + /*IsInit*/ true); // Deactivate the cleanup block. CGF.DeactivateCleanupBlock(cleanup); @@ -1084,7 +1085,8 @@ static void InitCatchParam(CodeGenFunction &CGF, CGF.EHStack.pushTerminate(); // Perform the copy construction. - CGF.EmitAggExpr(copyExpr, AggValueSlot::forAddr(ParamAddr, false, false)); + CGF.EmitAggExpr(copyExpr, AggValueSlot::forAddr(ParamAddr, Qualifiers(), + false)); // Leave the terminate scope. CGF.EHStack.popTerminate(); diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 525ad1b8f12..197bc678813 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -131,12 +131,12 @@ RValue CodeGenFunction::EmitAnyExprToTemp(const Expr *E) { /// location. void CodeGenFunction::EmitAnyExprToMem(const Expr *E, llvm::Value *Location, - bool IsLocationVolatile, + Qualifiers Quals, bool IsInit) { if (E->getType()->isAnyComplexType()) - EmitComplexExprIntoAddr(E, Location, IsLocationVolatile); + EmitComplexExprIntoAddr(E, Location, Quals.hasVolatile()); else if (hasAggregateLLVMType(E->getType())) - EmitAggExpr(E, AggValueSlot::forAddr(Location, IsLocationVolatile, IsInit)); + EmitAggExpr(E, AggValueSlot::forAddr(Location, Quals, IsInit)); else { RValue RV = RValue::get(EmitScalarExpr(E, /*Ignore*/ false)); LValue LV = MakeAddrLValue(Location, E->getType()); @@ -203,7 +203,10 @@ static llvm::Value * EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E, llvm::Value *&ReferenceTemporary, const CXXDestructorDecl *&ReferenceTemporaryDtor, + QualType &ObjCARCReferenceLifetimeType, const NamedDecl *InitializedDecl) { + ObjCARCReferenceLifetimeType = QualType(); + if (const CXXDefaultArgExpr *DAE = dyn_cast<CXXDefaultArgExpr>(E)) E = DAE->getExpr(); @@ -213,6 +216,7 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E, return EmitExprForReferenceBinding(CGF, TE->getSubExpr(), ReferenceTemporary, ReferenceTemporaryDtor, + ObjCARCReferenceLifetimeType, InitializedDecl); } @@ -279,12 +283,10 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E, !E->getType()->isAnyComplexType()) { ReferenceTemporary = CreateReferenceTemporary(CGF, E->getType(), InitializedDecl); - AggSlot = AggValueSlot::forAddr(ReferenceTemporary, false, + AggSlot = AggValueSlot::forAddr(ReferenceTemporary, Qualifiers(), InitializedDecl != 0); } - - RV = CGF.EmitAnyExpr(E, AggSlot); - + if (InitializedDecl) { // Get the destructor for the reference temporary. if (const RecordType *RT = E->getType()->getAs<RecordType>()) { @@ -292,8 +294,37 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E, if (!ClassDecl->hasTrivialDestructor()) ReferenceTemporaryDtor = ClassDecl->getDestructor(); } + else if (CGF.getContext().getLangOptions().ObjCAutoRefCount) { + if (const ValueDecl *InitVD = dyn_cast<ValueDecl>(InitializedDecl)) { + if (const ReferenceType *RefType + = InitVD->getType()->getAs<ReferenceType>()) { + QualType PointeeType = RefType->getPointeeType(); + if (PointeeType->isObjCLifetimeType() && + PointeeType.getObjCLifetime() != Qualifiers::OCL_ExplicitNone) { + // Objective-C++ ARC: We're binding a reference to + // lifetime-qualified type to a temporary, so we need to extend + // the lifetime of the temporary with appropriate retain/release/ + // autorelease calls. + ObjCARCReferenceLifetimeType = PointeeType; + + // Create a temporary variable that we can bind the reference to. + ReferenceTemporary = CreateReferenceTemporary(CGF, PointeeType, + InitializedDecl); + + unsigned Alignment = + CGF.getContext().getTypeAlignInChars(PointeeType).getQuantity(); + CGF.EmitScalarInit(E, InitVD, ReferenceTemporary, false, + PointeeType.isVolatileQualified(), + Alignment, PointeeType); + return ReferenceTemporary; + } + } + } + } } + RV = CGF.EmitAnyExpr(E, AggSlot); + // Check if need to perform derived-to-base casts and/or field accesses, to // get from the temporary object we created (and, potentially, for which we // extended the lifetime) to the subobject we're binding the reference to. @@ -361,26 +392,60 @@ CodeGenFunction::EmitReferenceBindingToExpr(const Expr *E, const NamedDecl *InitializedDecl) { llvm::Value *ReferenceTemporary = 0; const CXXDestructorDecl *ReferenceTemporaryDtor = 0; + QualType ObjCARCReferenceLifetimeType; llvm::Value *Value = EmitExprForReferenceBinding(*this, E, ReferenceTemporary, ReferenceTemporaryDtor, + ObjCARCReferenceLifetimeType, InitializedDecl); - if (!ReferenceTemporaryDtor) + if (!ReferenceTemporaryDtor && ObjCARCReferenceLifetimeType.isNull()) return RValue::get(Value); // Make sure to call the destructor for the reference temporary. - if (const VarDecl *VD = dyn_cast_or_null<VarDecl>(InitializedDecl)) { - if (VD->hasGlobalStorage()) { + const VarDecl *VD = dyn_cast_or_null<VarDecl>(InitializedDecl); + if (VD && VD->hasGlobalStorage()) { + if (ReferenceTemporaryDtor) { llvm::Constant *DtorFn = CGM.GetAddrOfCXXDestructor(ReferenceTemporaryDtor, Dtor_Complete); EmitCXXGlobalDtorRegistration(DtorFn, cast<llvm::Constant>(ReferenceTemporary)); - - return RValue::get(Value); + } else { + assert(!ObjCARCReferenceLifetimeType.isNull()); + // Note: We intentionally do not register a global "destructor" to + // release the object. } + + return RValue::get(Value); } - PushDestructorCleanup(ReferenceTemporaryDtor, ReferenceTemporary); - + if (ReferenceTemporaryDtor) + PushDestructorCleanup(ReferenceTemporaryDtor, ReferenceTemporary); + else { + switch (ObjCARCReferenceLifetimeType.getObjCLifetime()) { + case Qualifiers::OCL_None: + llvm_unreachable("Not a reference temporary that needs to be deallocated"); + break; + + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + // Nothing to do. + break; + + case Qualifiers::OCL_Strong: + PushARCReleaseCleanup(getARCCleanupKind(), ObjCARCReferenceLifetimeType, + ReferenceTemporary, + VD && VD->hasAttr<ObjCPreciseLifetimeAttr>()); + break; + + case Qualifiers::OCL_Weak: + // __weak objects always get EH cleanups; otherwise, exceptions + // could cause really nasty crashes instead of mere leaks. + PushARCWeakReleaseCleanup(NormalAndEHCleanup, + ObjCARCReferenceLifetimeType, + ReferenceTemporary); + break; + } + } + return RValue::get(Value); } @@ -599,6 +664,7 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) { case Expr::CXXDynamicCastExprClass: case Expr::CXXReinterpretCastExprClass: case Expr::CXXConstCastExprClass: + case Expr::ObjCBridgedCastExprClass: return EmitCastLValue(cast<CastExpr>(E)); } } @@ -668,6 +734,8 @@ RValue CodeGenFunction::EmitLoadOfLValue(LValue LV, QualType ExprType) { return RValue::get(CGM.getObjCRuntime().EmitObjCWeakRead(*this, AddrWeakObj)); } + if (LV.getQuals().getObjCLifetime() == Qualifiers::OCL_Weak) + return RValue::get(EmitARCLoadWeak(LV.getAddress())); if (LV.isSimple()) { llvm::Value *Ptr = LV.getAddress(); @@ -838,6 +906,31 @@ void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst, return EmitStoreThroughPropertyRefLValue(Src, Dst); } + // There's special magic for assigning into an ARC-qualified l-value. + if (Qualifiers::ObjCLifetime Lifetime = Dst.getQuals().getObjCLifetime()) { + switch (Lifetime) { + case Qualifiers::OCL_None: + llvm_unreachable("present but none"); + + case Qualifiers::OCL_ExplicitNone: + // nothing special + break; + + case Qualifiers::OCL_Strong: + EmitARCStoreStrong(Dst, Ty, Src.getScalarVal(), /*ignore*/ true); + return; + + case Qualifiers::OCL_Weak: + EmitARCStoreWeak(Dst.getAddress(), Src.getScalarVal(), /*ignore*/ true); + return; + + case Qualifiers::OCL_Autoreleasing: + Src = RValue::get(EmitObjCExtendObjectLifetime(Ty, Src.getScalarVal())); + // fall into the normal path + break; + } + } + if (Dst.isObjCWeak() && !Dst.isNonGC()) { // load of a __weak object. llvm::Value *LvalueDst = Dst.getAddress(); @@ -1113,7 +1206,12 @@ static void setObjCGCLValueClass(const ASTContext &Ctx, const Expr *E, setObjCGCLValueClass(Ctx, Exp->getSubExpr(), LV); return; } - + + if (const ObjCBridgedCastExpr *Exp = dyn_cast<ObjCBridgedCastExpr>(E)) { + setObjCGCLValueClass(Ctx, Exp->getSubExpr(), LV); + return; + } + if (const ArraySubscriptExpr *Exp = dyn_cast<ArraySubscriptExpr>(E)) { setObjCGCLValueClass(Ctx, Exp->getBase(), LV); if (LV.isObjCIvar() && !LV.isObjCArray()) @@ -1734,7 +1832,8 @@ LValue CodeGenFunction::EmitCompoundLiteralLValue(const CompoundLiteralExpr *E){ const Expr *InitExpr = E->getInitializer(); LValue Result = MakeAddrLValue(DeclPtr, E->getType()); - EmitAnyExprToMem(InitExpr, DeclPtr, /*Volatile*/ false, /*Init*/ true); + EmitAnyExprToMem(InitExpr, DeclPtr, E->getType().getQualifiers(), + /*Init*/ true); return Result; } @@ -1863,13 +1962,15 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { case CK_DerivedToBaseMemberPointer: case CK_BaseToDerivedMemberPointer: case CK_MemberPointerToBoolean: - case CK_AnyPointerToBlockPointerCast: { + case CK_AnyPointerToBlockPointerCast: + case CK_ObjCProduceObject: + case CK_ObjCConsumeObject: { // These casts only produce lvalues when we're binding a reference to a // temporary realized from a (converted) pure rvalue. Emit the expression // as a value, copy it into a temporary, and return an lvalue referring to // that temporary. llvm::Value *V = CreateMemTemp(E->getType(), "ref.temp"); - EmitAnyExprToMem(E, V, false, false); + EmitAnyExprToMem(E, V, E->getType().getQualifiers(), false); return MakeAddrLValue(V, E->getType()); } @@ -1988,13 +2089,60 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E, if (const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(TargetDecl)) return EmitCXXOperatorMemberCallExpr(CE, MD, ReturnValue); - if (isa<CXXPseudoDestructorExpr>(E->getCallee()->IgnoreParens())) { - // C++ [expr.pseudo]p1: - // The result shall only be used as the operand for the function call - // operator (), and the result of such a call has type void. The only - // effect is the evaluation of the postfix-expression before the dot or - // arrow. - EmitScalarExpr(E->getCallee()); + if (const CXXPseudoDestructorExpr *PseudoDtor + = dyn_cast<CXXPseudoDestructorExpr>(E->getCallee()->IgnoreParens())) { + QualType DestroyedType = PseudoDtor->getDestroyedType(); + if (getContext().getLangOptions().ObjCAutoRefCount && + DestroyedType->isObjCLifetimeType() && + (DestroyedType.getObjCLifetime() == Qualifiers::OCL_Strong || + DestroyedType.getObjCLifetime() == Qualifiers::OCL_Weak)) { + // Automatic Reference Counting: + // If the pseudo-expression names a retainable object with weak or strong + // lifetime, the object shall be released. + bool isNonGC = false; + Expr *BaseExpr = PseudoDtor->getBase(); + llvm::Value *BaseValue = NULL; + Qualifiers BaseQuals; + + // If this is s.x, emit s as an lvalue. If it is s->x, emit s as a scalar. + if (PseudoDtor->isArrow()) { + BaseValue = EmitScalarExpr(BaseExpr); + const PointerType *PTy = BaseExpr->getType()->getAs<PointerType>(); + BaseQuals = PTy->getPointeeType().getQualifiers(); + } else { + LValue BaseLV = EmitLValue(BaseExpr); + if (BaseLV.isNonGC()) + isNonGC = true; + BaseValue = BaseLV.getAddress(); + QualType BaseTy = BaseExpr->getType(); + BaseQuals = BaseTy.getQualifiers(); + } + + switch (PseudoDtor->getDestroyedType().getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + break; + + case Qualifiers::OCL_Strong: + EmitARCRelease(Builder.CreateLoad(BaseValue, + PseudoDtor->getDestroyedType().isVolatileQualified()), + /*precise*/ true); + break; + + case Qualifiers::OCL_Weak: + EmitARCDestroyWeak(BaseValue); + break; + } + } else { + // C++ [expr.pseudo]p1: + // The result shall only be used as the operand for the function call + // operator (), and the result of such a call has type void. The only + // effect is the evaluation of the postfix-expression before the dot or + // arrow. + EmitScalarExpr(E->getCallee()); + } + return RValue::get(0); } @@ -2016,9 +2164,25 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) { return EmitPointerToDataMemberBinaryExpr(E); assert(E->getOpcode() == BO_Assign && "unexpected binary l-value"); + + // Note that in all of these cases, __block variables need the RHS + // evaluated first just in case the variable gets moved by the RHS. if (!hasAggregateLLVMType(E->getType())) { - // __block variables need the RHS evaluated first. + switch (E->getLHS()->getType().getObjCLifetime()) { + case Qualifiers::OCL_Strong: + return EmitARCStoreStrong(E, /*ignored*/ false).first; + + case Qualifiers::OCL_Autoreleasing: + return EmitARCStoreAutoreleasing(E).first; + + // No reason to do any of these differently. + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Weak: + break; + } + RValue RV = EmitAnyExpr(E->getRHS()); LValue LV = EmitLValue(E->getLHS()); EmitStoreThroughLValue(RV, LV, E->getType()); diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index d8da642a6b3..6d34499f381 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -339,6 +339,8 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { case CK_IntegralComplexToBoolean: case CK_IntegralComplexCast: case CK_IntegralComplexToFloatingComplex: + case CK_ObjCProduceObject: + case CK_ObjCConsumeObject: llvm_unreachable("cast kind invalid for aggregate types"); } } @@ -570,8 +572,13 @@ AggExprEmitter::EmitInitializationToLValue(Expr* E, LValue LV, QualType T) { } else if (T->isAnyComplexType()) { CGF.EmitComplexExprIntoAddr(E, LV.getAddress(), false); } else if (CGF.hasAggregateLLVMType(T)) { - CGF.EmitAggExpr(E, AggValueSlot::forAddr(LV.getAddress(), false, true, + CGF.EmitAggExpr(E, AggValueSlot::forAddr(LV.getAddress(), + T.getQualifiers(), true, false, Dest.isZeroed())); + } else if (LV.isSimple()) { + CGF.EmitScalarInit(E, /*D=*/0, LV.getAddress(), /*Captured=*/false, + LV.isVolatileQualified(), LV.getAlignment(), + T); } else { CGF.EmitStoreThroughLValue(RValue::get(CGF.EmitScalarExpr(E)), LV, T); } @@ -636,6 +643,8 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { uint64_t NumArrayElements = AType->getNumElements(); QualType ElementType = CGF.getContext().getCanonicalType(E->getType()); ElementType = CGF.getContext().getAsArrayType(ElementType)->getElementType(); + ElementType = CGF.getContext().getQualifiedType(ElementType, + Dest.getQualifiers()); bool hasNonTrivialCXXConstructor = false; if (CGF.getContext().getLangOptions().CPlusPlus) @@ -645,8 +654,6 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { hasNonTrivialCXXConstructor = !RD->hasTrivialDefaultConstructor(); } - // FIXME: were we intentionally ignoring address spaces and GC attributes? - for (uint64_t i = 0; i != NumArrayElements; ++i) { // If we're done emitting initializers and the destination is known-zeroed // then we're done. @@ -873,8 +880,6 @@ static void CheckAggExprForMemSetUse(AggValueSlot &Slot, const Expr *E, /// /// \param IsInitializer - true if this evaluation is initializing an /// object whose lifetime is already being managed. -// -// FIXME: Take Qualifiers object. void CodeGenFunction::EmitAggExpr(const Expr *E, AggValueSlot Slot, bool IgnoreResult) { assert(E && hasAggregateLLVMType(E->getType()) && @@ -892,7 +897,7 @@ LValue CodeGenFunction::EmitAggExprToLValue(const Expr *E) { assert(hasAggregateLLVMType(E->getType()) && "Invalid argument!"); llvm::Value *Temp = CreateMemTemp(E->getType()); LValue LV = MakeAddrLValue(Temp, E->getType()); - EmitAggExpr(E, AggValueSlot::forAddr(Temp, LV.isVolatileQualified(), false)); + EmitAggExpr(E, AggValueSlot::forLValue(LV, false)); return LV; } @@ -954,7 +959,10 @@ void CodeGenFunction::EmitAggregateCopy(llvm::Value *DestPtr, llvm::Type::getInt8PtrTy(getLLVMContext(), SPT->getAddressSpace()); SrcPtr = Builder.CreateBitCast(SrcPtr, SBP, "tmp"); - if (const RecordType *RecordTy = Ty->getAs<RecordType>()) { + // Don't do any of the memmove_collectable tests if GC isn't set. + if (CGM.getLangOptions().getGCMode() == LangOptions::NonGC) { + // fall through + } else if (const RecordType *RecordTy = Ty->getAs<RecordType>()) { RecordDecl *Record = RecordTy->getDecl(); if (Record->hasObjectMember()) { CharUnits size = TypeInfo.first; @@ -964,7 +972,7 @@ void CodeGenFunction::EmitAggregateCopy(llvm::Value *DestPtr, SizeVal); return; } - } else if (getContext().getAsArrayType(Ty)) { + } else if (Ty->isArrayType()) { QualType BaseType = getContext().getBaseElementType(Ty); if (const RecordType *RecordTy = BaseType->getAs<RecordType>()) { if (RecordTy->getDecl()->hasObjectMember()) { diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 81fee677f61..434ca1b69fb 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -708,15 +708,14 @@ static void StoreAnyExprIntoOneUnit(CodeGenFunction &CGF, const CXXNewExpr *E, unsigned Alignment = CGF.getContext().getTypeAlignInChars(AllocType).getQuantity(); if (!CGF.hasAggregateLLVMType(AllocType)) - CGF.EmitStoreOfScalar(CGF.EmitScalarExpr(Init), NewPtr, - AllocType.isVolatileQualified(), Alignment, - AllocType); + CGF.EmitScalarInit(Init, 0, NewPtr, false, AllocType.isVolatileQualified(), + Alignment, AllocType); else if (AllocType->isAnyComplexType()) CGF.EmitComplexExprIntoAddr(Init, NewPtr, AllocType.isVolatileQualified()); else { AggValueSlot Slot - = AggValueSlot::forAddr(NewPtr, AllocType.isVolatileQualified(), true); + = AggValueSlot::forAddr(NewPtr, AllocType.getQualifiers(), true); CGF.EmitAggExpr(Init, Slot); } } @@ -1075,7 +1074,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { // CXXNewExpr::shouldNullCheckAllocation()) and we have an // interesting initializer. bool nullCheck = allocatorType->isNothrow(getContext()) && - !(allocType->isPODType() && !E->hasInitializer()); + !(allocType.isPODType(getContext()) && !E->hasInitializer()); llvm::BasicBlock *nullCheckBB = 0; llvm::BasicBlock *contBB = 0; @@ -1247,7 +1246,29 @@ static void EmitObjectDelete(CodeGenFunction &CGF, if (Dtor) CGF.EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false, Ptr); + else if (CGF.getLangOptions().ObjCAutoRefCount && + ElementType->isObjCLifetimeType()) { + switch (ElementType.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + break; + case Qualifiers::OCL_Strong: { + // Load the pointer value. + llvm::Value *PtrValue = CGF.Builder.CreateLoad(Ptr, + ElementType.isVolatileQualified()); + + CGF.EmitARCRelease(PtrValue, /*precise*/ true); + break; + } + + case Qualifiers::OCL_Weak: + CGF.EmitARCDestroyWeak(Ptr); + break; + } + } + CGF.PopCleanupBlock(); } @@ -1339,6 +1360,65 @@ static void EmitArrayDelete(CodeGenFunction &CGF, " for a class with destructor"); CGF.EmitCXXAggrDestructorCall(RD->getDestructor(), NumElements, Ptr); } + } else if (CGF.getLangOptions().ObjCAutoRefCount && + ElementType->isObjCLifetimeType() && + (ElementType.getObjCLifetime() == Qualifiers::OCL_Strong || + ElementType.getObjCLifetime() == Qualifiers::OCL_Weak)) { + bool IsStrong = ElementType.getObjCLifetime() == Qualifiers::OCL_Strong; + const llvm::Type *SizeLTy = CGF.ConvertType(CGF.getContext().getSizeType()); + llvm::Value *One = llvm::ConstantInt::get(SizeLTy, 1); + + // Create a temporary for the loop index and initialize it with count of + // array elements. + llvm::Value *IndexPtr = CGF.CreateTempAlloca(SizeLTy, "loop.index"); + + // Store the number of elements in the index pointer. + CGF.Builder.CreateStore(NumElements, IndexPtr); + + // Start the loop with a block that tests the condition. + llvm::BasicBlock *CondBlock = CGF.createBasicBlock("for.cond"); + llvm::BasicBlock *AfterFor = CGF.createBasicBlock("for.end"); + + CGF.EmitBlock(CondBlock); + + llvm::BasicBlock *ForBody = CGF.createBasicBlock("for.body"); + + // Generate: if (loop-index != 0 fall to the loop body, + // otherwise, go to the block after the for-loop. + llvm::Value* zeroConstant = llvm::Constant::getNullValue(SizeLTy); + llvm::Value *Counter = CGF.Builder.CreateLoad(IndexPtr); + llvm::Value *IsNE = CGF.Builder.CreateICmpNE(Counter, zeroConstant, + "isne"); + // If the condition is true, execute the body. + CGF.Builder.CreateCondBr(IsNE, ForBody, AfterFor); + + CGF.EmitBlock(ForBody); + + llvm::BasicBlock *ContinueBlock = CGF.createBasicBlock("for.inc"); + // Inside the loop body, emit the constructor call on the array element. + Counter = CGF.Builder.CreateLoad(IndexPtr); + Counter = CGF.Builder.CreateSub(Counter, One); + llvm::Value *Address = CGF.Builder.CreateInBoundsGEP(Ptr, Counter, + "arrayidx"); + if (IsStrong) + CGF.EmitARCRelease(CGF.Builder.CreateLoad(Address, + ElementType.isVolatileQualified()), + /*precise*/ true); + else + CGF.EmitARCDestroyWeak(Address); + + CGF.EmitBlock(ContinueBlock); + + // Emit the decrement of the loop counter. + Counter = CGF.Builder.CreateLoad(IndexPtr); + Counter = CGF.Builder.CreateSub(Counter, One, "dec"); + CGF.Builder.CreateStore(Counter, IndexPtr); + + // Finally, branch back up to the condition for the next iteration. + CGF.EmitBranch(CondBlock); + + // Emit the fall-through block. + CGF.EmitBlock(AfterFor, true); } CGF.PopCleanupBlock(); diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index da37bd5b0a1..75e566110cd 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -570,6 +570,8 @@ public: case CK_GetObjCProperty: case CK_ToVoid: case CK_Dynamic: + case CK_ObjCProduceObject: + case CK_ObjCConsumeObject: return 0; // These might need to be supported for constexpr. diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index dff7bf45e0c..6e42da8e01a 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -1106,7 +1106,12 @@ Value *ScalarExprEmitter::EmitCastExpr(CastExpr *CE) { // function pointers on Itanium and ARM). return CGF.CGM.getCXXABI().EmitMemberPointerConversion(CGF, CE, Src); } - + + case CK_ObjCProduceObject: + return CGF.EmitARCRetainScalarExpr(E); + case CK_ObjCConsumeObject: + return CGF.EmitObjCConsumeObject(E->getType(), Visit(E)); + case CK_FloatingRealToComplex: case CK_FloatingComplexCast: case CK_IntegralRealToComplex: @@ -2228,20 +2233,42 @@ Value *ScalarExprEmitter::EmitCompare(const BinaryOperator *E,unsigned UICmpOpc, Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) { bool Ignore = TestAndClearIgnoreResultAssign(); - // __block variables need to have the rhs evaluated first, plus this should - // improve codegen just a little. - Value *RHS = Visit(E->getRHS()); - LValue LHS = EmitCheckedLValue(E->getLHS()); - - // Store the value into the LHS. Bit-fields are handled specially - // because the result is altered by the store, i.e., [C99 6.5.16p1] - // 'An assignment expression has the value of the left operand after - // the assignment...'. - if (LHS.isBitField()) - CGF.EmitStoreThroughBitfieldLValue(RValue::get(RHS), LHS, E->getType(), - &RHS); - else - CGF.EmitStoreThroughLValue(RValue::get(RHS), LHS, E->getType()); + Value *RHS; + LValue LHS; + + switch (E->getLHS()->getType().getObjCLifetime()) { + case Qualifiers::OCL_Strong: + llvm::tie(LHS, RHS) = CGF.EmitARCStoreStrong(E, Ignore); + break; + + case Qualifiers::OCL_Autoreleasing: + llvm::tie(LHS,RHS) = CGF.EmitARCStoreAutoreleasing(E); + break; + + case Qualifiers::OCL_Weak: + RHS = Visit(E->getRHS()); + LHS = EmitCheckedLValue(E->getLHS()); + RHS = CGF.EmitARCStoreWeak(LHS.getAddress(), RHS, Ignore); + break; + + // No reason to do any of these differently. + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + // __block variables need to have the rhs evaluated first, plus + // this should improve codegen just a little. + RHS = Visit(E->getRHS()); + LHS = EmitCheckedLValue(E->getLHS()); + + // Store the value into the LHS. Bit-fields are handled specially + // because the result is altered by the store, i.e., [C99 6.5.16p1] + // 'An assignment expression has the value of the left operand after + // the assignment...'. + if (LHS.isBitField()) + CGF.EmitStoreThroughBitfieldLValue(RValue::get(RHS), LHS, E->getType(), + &RHS); + else + CGF.EmitStoreThroughLValue(RValue::get(RHS), LHS, E->getType()); + } // If the result is clearly ignored, return now. if (Ignore) diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp index cdb15bfd83e..cdc2fffd942 100644 --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -15,15 +15,29 @@ #include "CGObjCRuntime.h" #include "CodeGenFunction.h" #include "CodeGenModule.h" +#include "TargetInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/StmtObjC.h" #include "clang/Basic/Diagnostic.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Target/TargetData.h" +#include "llvm/InlineAsm.h" using namespace clang; using namespace CodeGen; +typedef llvm::PointerIntPair<llvm::Value*,1,bool> TryEmitResult; +static TryEmitResult +tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e); + +/// Given the address of a variable of pointer type, find the correct +/// null to store into it. +static llvm::Constant *getNullForVariable(llvm::Value *addr) { + const llvm::Type *type = + cast<llvm::PointerType>(addr->getType())->getElementType(); + return llvm::ConstantPointerNull::get(cast<llvm::PointerType>(type)); +} + /// Emits an instance of NSConstantString representing the object. llvm::Value *CodeGenFunction::EmitObjCStringLiteral(const ObjCStringLiteral *E) { @@ -55,6 +69,7 @@ static RValue AdjustRelatedResultType(CodeGenFunction &CGF, RValue Result) { if (!Method) return Result; + if (!Method->hasRelatedResultType() || CGF.getContext().hasSameType(E->getType(), Method->getResultType()) || !Result.isScalar()) @@ -71,6 +86,18 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, // implementation vary between runtimes. We can get the receiver and // arguments in generic code. + bool isDelegateInit = E->isDelegateInitCall(); + + // We don't retain the receiver in delegate init calls, and this is + // safe because the receiver value is always loaded from 'self', + // which we zero out. We don't want to Block_copy block receivers, + // though. + bool retainSelf = + (!isDelegateInit && + CGM.getLangOptions().ObjCAutoRefCount && + E->getMethodDecl() && + E->getMethodDecl()->hasAttr<NSConsumesSelfAttr>()); + CGObjCRuntime &Runtime = CGM.getObjCRuntime(); bool isSuperMessage = false; bool isClassMessage = false; @@ -80,8 +107,15 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, llvm::Value *Receiver = 0; switch (E->getReceiverKind()) { case ObjCMessageExpr::Instance: - Receiver = EmitScalarExpr(E->getInstanceReceiver()); ReceiverType = E->getInstanceReceiver()->getType(); + if (retainSelf) { + TryEmitResult ter = tryEmitARCRetainScalarExpr(*this, + E->getInstanceReceiver()); + Receiver = ter.getPointer(); + if (!ter.getInt()) + Receiver = EmitARCRetainNonBlock(Receiver); + } else + Receiver = EmitScalarExpr(E->getInstanceReceiver()); break; case ObjCMessageExpr::Class: { @@ -92,6 +126,9 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, assert(OID && "Invalid Objective-C class message send"); Receiver = Runtime.GetClass(Builder, OID); isClassMessage = true; + + if (retainSelf) + Receiver = EmitARCRetainNonBlock(Receiver); break; } @@ -99,6 +136,9 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, ReceiverType = E->getSuperType(); Receiver = LoadObjCSelf(); isSuperMessage = true; + + if (retainSelf) + Receiver = EmitARCRetainNonBlock(Receiver); break; case ObjCMessageExpr::SuperClass: @@ -106,14 +146,36 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, Receiver = LoadObjCSelf(); isSuperMessage = true; isClassMessage = true; + + if (retainSelf) + Receiver = EmitARCRetainNonBlock(Receiver); break; } + QualType ResultType = + E->getMethodDecl() ? E->getMethodDecl()->getResultType() : E->getType(); + CallArgList Args; EmitCallArgs(Args, E->getMethodDecl(), E->arg_begin(), E->arg_end()); - QualType ResultType = - E->getMethodDecl() ? E->getMethodDecl()->getResultType() : E->getType(); + // For delegate init calls in ARC, do an unsafe store of null into + // self. This represents the call taking direct ownership of that + // value. We have to do this after emitting the other call + // arguments because they might also reference self, but we don't + // have to worry about any of them modifying self because that would + // be an undefined read and write of an object in unordered + // expressions. + if (isDelegateInit) { + assert(getLangOptions().ObjCAutoRefCount && + "delegate init calls should only be marked in ARC"); + + // Do an unsafe store of null into self. + llvm::Value *selfAddr = + LocalDeclMap[cast<ObjCMethodDecl>(CurCodeDecl)->getSelfDecl()]; + assert(selfAddr && "no self entry for a delegate init call?"); + + Builder.CreateStore(getNullForVariable(selfAddr), selfAddr); + } RValue result; if (isSuperMessage) { @@ -134,10 +196,52 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, Receiver, Args, OID, E->getMethodDecl()); } - + + // For delegate init calls in ARC, implicitly store the result of + // the call back into self. This takes ownership of the value. + if (isDelegateInit) { + llvm::Value *selfAddr = + LocalDeclMap[cast<ObjCMethodDecl>(CurCodeDecl)->getSelfDecl()]; + llvm::Value *newSelf = result.getScalarVal(); + + // The delegate return type isn't necessarily a matching type; in + // fact, it's quite likely to be 'id'. + const llvm::Type *selfTy = + cast<llvm::PointerType>(selfAddr->getType())->getElementType(); + newSelf = Builder.CreateBitCast(newSelf, selfTy); + + Builder.CreateStore(newSelf, selfAddr); + } + return AdjustRelatedResultType(*this, E, E->getMethodDecl(), result); } +namespace { +struct FinishARCDealloc : EHScopeStack::Cleanup { + void Emit(CodeGenFunction &CGF, bool isForEH) { + const ObjCMethodDecl *method = cast<ObjCMethodDecl>(CGF.CurCodeDecl); + const ObjCImplementationDecl *impl + = cast<ObjCImplementationDecl>(method->getDeclContext()); + const ObjCInterfaceDecl *iface = impl->getClassInterface(); + if (!iface->getSuperClass()) return; + + // Call [super dealloc] if we have a superclass. + llvm::Value *self = CGF.LoadObjCSelf(); + + CallArgList args; + CGF.CGM.getObjCRuntime().GenerateMessageSendSuper(CGF, ReturnValueSlot(), + CGF.getContext().VoidTy, + method->getSelector(), + iface, + /*is category*/ false, + self, + /*is class msg*/ false, + args, + method); + } +}; +} + /// StartObjCMethod - Begin emission of an ObjCMethod. This generates /// the LLVM function and sets the other context used by /// CodeGenFunction. @@ -164,8 +268,21 @@ void CodeGenFunction::StartObjCMethod(const ObjCMethodDecl *OMD, CurGD = OMD; StartFunction(OMD, OMD->getResultType(), Fn, FI, args, StartLoc); + + // In ARC, certain methods get an extra cleanup. + if (CGM.getLangOptions().ObjCAutoRefCount && + OMD->isInstanceMethod() && + OMD->getSelector().isUnarySelector()) { + const IdentifierInfo *ident = + OMD->getSelector().getIdentifierInfoForSlot(0); + if (ident->isStr("dealloc")) + EHStack.pushCleanup<FinishARCDealloc>(getARCCleanupKind()); + } } +static llvm::Value *emitARCRetainLoadOfScalar(CodeGenFunction &CGF, + LValue lvalue, QualType type); + void CodeGenFunction::GenerateObjCGetterBody(ObjCIvarDecl *Ivar, bool IsAtomic, bool IsStrong) { LValue LV = EmitLValueForIvar(TypeOfSelfObject(), LoadObjCSelf(), @@ -269,6 +386,9 @@ void CodeGenFunction::GenerateObjCGetter(ObjCImplementationDecl *IMP, RV = RValue::get(Builder.CreateBitCast(RV.getScalarVal(), Types.ConvertType(PD->getType()))); EmitReturnOfRValue(RV, PD->getType()); + + // objc_getProperty does an autorelease, so we should suppress ours. + AutoreleaseResult = false; } else { const llvm::Triple &Triple = getContext().Target.getTriple(); QualType IVART = Ivar->getType(); @@ -347,17 +467,23 @@ void CodeGenFunction::GenerateObjCGetter(ObjCImplementationDecl *IMP, else { LValue LV = EmitLValueForIvar(TypeOfSelfObject(), LoadObjCSelf(), Ivar, 0); - if (PD->getType()->isReferenceType()) { - RValue RV = RValue::get(LV.getAddress()); - EmitReturnOfRValue(RV, PD->getType()); - } - else { - CodeGenTypes &Types = CGM.getTypes(); - RValue RV = EmitLoadOfLValue(LV, IVART); - RV = RValue::get(Builder.CreateBitCast(RV.getScalarVal(), - Types.ConvertType(PD->getType()))); - EmitReturnOfRValue(RV, PD->getType()); + QualType propType = PD->getType(); + + llvm::Value *value; + if (propType->isReferenceType()) { + value = LV.getAddress(); + } else { + // In ARC, we want to emit this retained. + if (getLangOptions().ObjCAutoRefCount && + PD->getType()->isObjCRetainableType()) + value = emitARCRetainLoadOfScalar(*this, LV, IVART); + else + value = EmitLoadOfLValue(LV, IVART).getScalarVal(); + + value = Builder.CreateBitCast(value, ConvertType(propType)); } + + EmitReturnOfRValue(RValue::get(value), propType); } } @@ -597,6 +723,11 @@ namespace { }; } +static void pushReleaseForIvar(CodeGenFunction &CGF, ObjCIvarDecl *ivar, + llvm::Value *self); +static void pushWeakReleaseForIvar(CodeGenFunction &CGF, ObjCIvarDecl *ivar, + llvm::Value *self); + static void emitCXXDestructMethod(CodeGenFunction &CGF, ObjCImplementationDecl *impl) { CodeGenFunction::RunCleanupsScope scope(CGF); @@ -631,6 +762,14 @@ static void emitCXXDestructMethod(CodeGenFunction &CGF, CGF.EHStack.pushCleanup<CallIvarDtor>(NormalAndEHCleanup, ivar, self); break; + + case QualType::DK_objc_strong_lifetime: + pushReleaseForIvar(CGF, ivar, self); + break; + + case QualType::DK_objc_weak_lifetime: + pushWeakReleaseForIvar(CGF, ivar, self); + break; } } @@ -645,6 +784,9 @@ void CodeGenFunction::GenerateObjCCtorDtorMethod(ObjCImplementationDecl *IMP, // Emit .cxx_construct. if (ctor) { + // Suppress the final autorelease in ARC. + AutoreleaseResult = false; + llvm::SmallVector<CXXCtorInitializer *, 8> IvarInitializers; for (ObjCImplementationDecl::init_const_iterator B = IMP->init_begin(), E = IMP->init_end(); B != E; ++B) { @@ -747,6 +889,16 @@ RValue CodeGenFunction::EmitLoadOfPropertyRefLValue(LValue LV, llvm::Value *Receiver = LV.getPropertyRefBaseAddr(); + if (CGM.getLangOptions().ObjCAutoRefCount) { + QualType receiverType; + if (E->isSuperReceiver()) + receiverType = E->getSuperReceiverType(); + else if (E->isClassReceiver()) + receiverType = getContext().getObjCClassType(); + else + receiverType = E->getBase()->getType(); + } + // Accesses to 'super' follow a different code path. if (E->isSuperReceiver()) return AdjustRelatedResultType(*this, E, method, @@ -757,9 +909,9 @@ RValue CodeGenFunction::EmitLoadOfPropertyRefLValue(LValue LV, const ObjCInterfaceDecl *ReceiverClass = (E->isClassReceiver() ? E->getClassReceiver() : 0); return AdjustRelatedResultType(*this, E, method, - CGM.getObjCRuntime(). - GenerateMessageSend(*this, Return, ResultType, S, - Receiver, CallArgList(), ReceiverClass)); + CGM.getObjCRuntime(). + GenerateMessageSend(*this, Return, ResultType, S, + Receiver, CallArgList(), ReceiverClass)); } void CodeGenFunction::EmitStoreThroughPropertyRefLValue(RValue Src, @@ -1072,4 +1224,1197 @@ void CodeGenFunction::EmitObjCAtSynchronizedStmt( CGM.getObjCRuntime().EmitSynchronizedStmt(*this, S); } +/// Produce the code for a CK_ObjCProduceObject. Just does a +/// primitive retain. +llvm::Value *CodeGenFunction::EmitObjCProduceObject(QualType type, + llvm::Value *value) { + return EmitARCRetain(type, value); +} + +namespace { + struct CallObjCRelease : EHScopeStack::Cleanup { + CallObjCRelease(QualType type, llvm::Value *ptr, llvm::Value *condition) + : type(type), ptr(ptr), condition(condition) {} + QualType type; + llvm::Value *ptr; + llvm::Value *condition; + + void Emit(CodeGenFunction &CGF, bool forEH) { + llvm::Value *object; + + // If we're in a conditional branch, we had to stash away in an + // alloca the pointer to be released. + llvm::BasicBlock *cont = 0; + if (condition) { + llvm::BasicBlock *release = CGF.createBasicBlock("release.yes"); + cont = CGF.createBasicBlock("release.cont"); + + llvm::Value *cond = CGF.Builder.CreateLoad(condition); + CGF.Builder.CreateCondBr(cond, release, cont); + CGF.EmitBlock(release); + object = CGF.Builder.CreateLoad(ptr); + } else { + object = ptr; + } + + CGF.EmitARCRelease(object, /*precise*/ true); + + if (cont) CGF.EmitBlock(cont); + } + }; +} + +/// Produce the code for a CK_ObjCConsumeObject. Does a primitive +/// release at the end of the full-expression. +llvm::Value *CodeGenFunction::EmitObjCConsumeObject(QualType type, + llvm::Value *object) { + // If we're in a conditional branch, we need to make the cleanup + // conditional. FIXME: this really needs to be supported by the + // environment. + llvm::AllocaInst *cond; + llvm::Value *ptr; + if (isInConditionalBranch()) { + cond = CreateTempAlloca(Builder.getInt1Ty(), "release.cond"); + ptr = CreateTempAlloca(object->getType(), "release.value"); + + // The alloca is false until we get here. + // FIXME: er. doesn't this need to be set at the start of the condition? + InitTempAlloca(cond, Builder.getFalse()); + + // Then it turns true. + Builder.CreateStore(Builder.getTrue(), cond); + Builder.CreateStore(object, ptr); + } else { + cond = 0; + ptr = object; + } + + EHStack.pushCleanup<CallObjCRelease>(getARCCleanupKind(), type, ptr, cond); + return object; +} + +llvm::Value *CodeGenFunction::EmitObjCExtendObjectLifetime(QualType type, + llvm::Value *value) { + return EmitARCRetainAutorelease(type, value); +} + + +static llvm::Constant *createARCRuntimeFunction(CodeGenModule &CGM, + const llvm::FunctionType *type, + llvm::StringRef fnName) { + llvm::Constant *fn = CGM.CreateRuntimeFunction(type, fnName); + + // In -fobjc-no-arc-runtime, emit weak references to the runtime + // support library. + if (CGM.getLangOptions().ObjCNoAutoRefCountRuntime) + if (llvm::Function *f = dyn_cast<llvm::Function>(fn)) + f->setLinkage(llvm::Function::ExternalWeakLinkage); + + return fn; +} + +/// Perform an operation having the signature +/// i8* (i8*) +/// where a null input causes a no-op and returns null. +static llvm::Value *emitARCValueOperation(CodeGenFunction &CGF, + llvm::Value *value, + llvm::Constant *&fn, + llvm::StringRef fnName) { + if (isa<llvm::ConstantPointerNull>(value)) return value; + + if (!fn) { + std::vector<const llvm::Type*> args(1, CGF.Int8PtrTy); + const llvm::FunctionType *fnType = + llvm::FunctionType::get(CGF.Int8PtrTy, args, false); + fn = createARCRuntimeFunction(CGF.CGM, fnType, fnName); + } + + // Cast the argument to 'id'. + const llvm::Type *origType = value->getType(); + value = CGF.Builder.CreateBitCast(value, CGF.Int8PtrTy); + + // Call the function. + llvm::CallInst *call = CGF.Builder.CreateCall(fn, value); + call->setDoesNotThrow(); + + // Cast the result back to the original type. + return CGF.Builder.CreateBitCast(call, origType); +} + +/// Perform an operation having the following signature: +/// i8* (i8**) +static llvm::Value *emitARCLoadOperation(CodeGenFunction &CGF, + llvm::Value *addr, + llvm::Constant *&fn, + llvm::StringRef fnName) { + if (!fn) { + std::vector<const llvm::Type*> args(1, CGF.Int8PtrPtrTy); + const llvm::FunctionType *fnType = + llvm::FunctionType::get(CGF.Int8PtrTy, args, false); + fn = createARCRuntimeFunction(CGF.CGM, fnType, fnName); + } + + // Cast the argument to 'id*'. + const llvm::Type *origType = addr->getType(); + addr = CGF.Builder.CreateBitCast(addr, CGF.Int8PtrPtrTy); + + // Call the function. + llvm::CallInst *call = CGF.Builder.CreateCall(fn, addr); + call->setDoesNotThrow(); + + // Cast the result back to a dereference of the original type. + llvm::Value *result = call; + if (origType != CGF.Int8PtrPtrTy) + result = CGF.Builder.CreateBitCast(result, + cast<llvm::PointerType>(origType)->getElementType()); + + return result; +} + +/// Perform an operation having the following signature: +/// i8* (i8**, i8*) +static llvm::Value *emitARCStoreOperation(CodeGenFunction &CGF, + llvm::Value *addr, + llvm::Value *value, + llvm::Constant *&fn, + llvm::StringRef fnName, + bool ignored) { + assert(cast<llvm::PointerType>(addr->getType())->getElementType() + == value->getType()); + + if (!fn) { + std::vector<const llvm::Type*> argTypes(2); + argTypes[0] = CGF.Int8PtrPtrTy; + argTypes[1] = CGF.Int8PtrTy; + + const llvm::FunctionType *fnType + = llvm::FunctionType::get(CGF.Int8PtrTy, argTypes, false); + fn = createARCRuntimeFunction(CGF.CGM, fnType, fnName); + } + + const llvm::Type *origType = value->getType(); + + addr = CGF.Builder.CreateBitCast(addr, CGF.Int8PtrPtrTy); + value = CGF.Builder.CreateBitCast(value, CGF.Int8PtrTy); + + llvm::CallInst *result = CGF.Builder.CreateCall2(fn, addr, value); + result->setDoesNotThrow(); + + if (ignored) return 0; + + return CGF.Builder.CreateBitCast(result, origType); +} + +/// Perform an operation having the following signature: +/// void (i8**, i8**) +static void emitARCCopyOperation(CodeGenFunction &CGF, + llvm::Value *dst, + llvm::Value *src, + llvm::Constant *&fn, + llvm::StringRef fnName) { + assert(dst->getType() == src->getType()); + + if (!fn) { + std::vector<const llvm::Type*> argTypes(2, CGF.Int8PtrPtrTy); + const llvm::FunctionType *fnType + = llvm::FunctionType::get(CGF.Builder.getVoidTy(), argTypes, false); + fn = createARCRuntimeFunction(CGF.CGM, fnType, fnName); + } + + dst = CGF.Builder.CreateBitCast(dst, CGF.Int8PtrPtrTy); + src = CGF.Builder.CreateBitCast(src, CGF.Int8PtrPtrTy); + + llvm::CallInst *result = CGF.Builder.CreateCall2(fn, dst, src); + result->setDoesNotThrow(); +} + +/// Produce the code to do a retain. Based on the type, calls one of: +/// call i8* @objc_retain(i8* %value) +/// call i8* @objc_retainBlock(i8* %value) +llvm::Value *CodeGenFunction::EmitARCRetain(QualType type, llvm::Value *value) { + if (type->isBlockPointerType()) + return EmitARCRetainBlock(value); + else + return EmitARCRetainNonBlock(value); +} + +/// Retain the given object, with normal retain semantics. +/// call i8* @objc_retain(i8* %value) +llvm::Value *CodeGenFunction::EmitARCRetainNonBlock(llvm::Value *value) { + return emitARCValueOperation(*this, value, + CGM.getARCEntrypoints().objc_retain, + "objc_retain"); +} + +/// Retain the given block, with _Block_copy semantics. +/// call i8* @objc_retainBlock(i8* %value) +llvm::Value *CodeGenFunction::EmitARCRetainBlock(llvm::Value *value) { + return emitARCValueOperation(*this, value, + CGM.getARCEntrypoints().objc_retainBlock, + "objc_retainBlock"); +} + +/// Retain the given object which is the result of a function call. +/// call i8* @objc_retainAutoreleasedReturnValue(i8* %value) +/// +/// Yes, this function name is one character away from a different +/// call with completely different semantics. +llvm::Value * +CodeGenFunction::EmitARCRetainAutoreleasedReturnValue(llvm::Value *value) { + // Fetch the void(void) inline asm which marks that we're going to + // retain the autoreleased return value. + llvm::InlineAsm *&marker + = CGM.getARCEntrypoints().retainAutoreleasedReturnValueMarker; + if (!marker) { + llvm::StringRef assembly + = CGM.getTargetCodeGenInfo() + .getARCRetainAutoreleasedReturnValueMarker(); + + // If we have an empty assembly string, there's nothing to do. + if (assembly.empty()) { + + // Otherwise, at -O0, build an inline asm that we're going to call + // in a moment. + } else if (CGM.getCodeGenOpts().OptimizationLevel == 0) { + llvm::FunctionType *type = + llvm::FunctionType::get(llvm::Type::getVoidTy(getLLVMContext()), + /*variadic*/ false); + + marker = llvm::InlineAsm::get(type, assembly, "", /*sideeffects*/ true); + + // If we're at -O1 and above, we don't want to litter the code + // with this marker yet, so leave a breadcrumb for the ARC + // optimizer to pick up. + } else { + llvm::NamedMDNode *metadata = + CGM.getModule().getOrInsertNamedMetadata( + "clang.arc.retainAutoreleasedReturnValueMarker"); + assert(metadata->getNumOperands() <= 1); + if (metadata->getNumOperands() == 0) { + llvm::Value *string = llvm::MDString::get(getLLVMContext(), assembly); + llvm::Value *args[] = { string }; + metadata->addOperand(llvm::MDNode::get(getLLVMContext(), args)); + } + } + } + + // Call the marker asm if we made one, which we do only at -O0. + if (marker) Builder.CreateCall(marker); + + return emitARCValueOperation(*this, value, + CGM.getARCEntrypoints().objc_retainAutoreleasedReturnValue, + "objc_retainAutoreleasedReturnValue"); +} + +/// Release the given object. +/// call void @objc_release(i8* %value) +void CodeGenFunction::EmitARCRelease(llvm::Value *value, bool precise) { + if (isa<llvm::ConstantPointerNull>(value)) return; + + llvm::Constant *&fn = CGM.getARCEntrypoints().objc_release; + if (!fn) { + std::vector<const llvm::Type*> args(1, Int8PtrTy); + const llvm::FunctionType *fnType = + llvm::FunctionType::get(Builder.getVoidTy(), args, false); + fn = createARCRuntimeFunction(CGM, fnType, "objc_release"); + } + + // Cast the argument to 'id'. + value = Builder.CreateBitCast(value, Int8PtrTy); + + // Call objc_release. + llvm::CallInst *call = Builder.CreateCall(fn, value); + call->setDoesNotThrow(); + + if (!precise) { + llvm::SmallVector<llvm::Value*,1> args; + call->setMetadata("clang.imprecise_release", + llvm::MDNode::get(Builder.getContext(), args)); + } +} + +/// Store into a strong object. Always calls this: +/// call void @objc_storeStrong(i8** %addr, i8* %value) +llvm::Value *CodeGenFunction::EmitARCStoreStrongCall(llvm::Value *addr, + llvm::Value *value, + bool ignored) { + assert(cast<llvm::PointerType>(addr->getType())->getElementType() + == value->getType()); + + llvm::Constant *&fn = CGM.getARCEntrypoints().objc_storeStrong; + if (!fn) { + const llvm::Type *argTypes[] = { Int8PtrPtrTy, Int8PtrTy }; + const llvm::FunctionType *fnType + = llvm::FunctionType::get(Builder.getVoidTy(), argTypes, false); + fn = createARCRuntimeFunction(CGM, fnType, "objc_storeStrong"); + } + + addr = Builder.CreateBitCast(addr, Int8PtrPtrTy); + llvm::Value *castValue = Builder.CreateBitCast(value, Int8PtrTy); + + Builder.CreateCall2(fn, addr, castValue)->setDoesNotThrow(); + + if (ignored) return 0; + return value; +} + +/// Store into a strong object. Sometimes calls this: +/// call void @objc_storeStrong(i8** %addr, i8* %value) +/// Other times, breaks it down into components. +llvm::Value *CodeGenFunction::EmitARCStoreStrong(LValue dst, QualType type, + llvm::Value *newValue, + bool ignored) { + bool isBlock = type->isBlockPointerType(); + + // Use a store barrier at -O0 unless this is a block type or the + // lvalue is inadequately aligned. + if (shouldUseFusedARCCalls() && + !isBlock && + !(dst.getAlignment() && dst.getAlignment() < PointerAlignInBytes)) { + return EmitARCStoreStrongCall(dst.getAddress(), newValue, ignored); + } + + // Otherwise, split it out. + + // Retain the new value. + newValue = EmitARCRetain(type, newValue); + + // Read the old value. + llvm::Value *oldValue = + EmitLoadOfScalar(dst.getAddress(), dst.isVolatileQualified(), + dst.getAlignment(), type, dst.getTBAAInfo()); + + // Store. We do this before the release so that any deallocs won't + // see the old value. + EmitStoreOfScalar(newValue, dst.getAddress(), + dst.isVolatileQualified(), dst.getAlignment(), + type, dst.getTBAAInfo()); + + // Finally, release the old value. + EmitARCRelease(oldValue, /*precise*/ false); + + return newValue; +} + +/// Autorelease the given object. +/// call i8* @objc_autorelease(i8* %value) +llvm::Value *CodeGenFunction::EmitARCAutorelease(llvm::Value *value) { + return emitARCValueOperation(*this, value, + CGM.getARCEntrypoints().objc_autorelease, + "objc_autorelease"); +} + +/// Autorelease the given object. +/// call i8* @objc_autoreleaseReturnValue(i8* %value) +llvm::Value * +CodeGenFunction::EmitARCAutoreleaseReturnValue(llvm::Value *value) { + return emitARCValueOperation(*this, value, + CGM.getARCEntrypoints().objc_autoreleaseReturnValue, + "objc_autoreleaseReturnValue"); +} + +/// Do a fused retain/autorelease of the given object. +/// call i8* @objc_retainAutoreleaseReturnValue(i8* %value) +llvm::Value * +CodeGenFunction::EmitARCRetainAutoreleaseReturnValue(llvm::Value *value) { + return emitARCValueOperation(*this, value, + CGM.getARCEntrypoints().objc_retainAutoreleaseReturnValue, + "objc_retainAutoreleaseReturnValue"); +} + +/// Do a fused retain/autorelease of the given object. +/// call i8* @objc_retainAutorelease(i8* %value) +/// or +/// %retain = call i8* @objc_retainBlock(i8* %value) +/// call i8* @objc_autorelease(i8* %retain) +llvm::Value *CodeGenFunction::EmitARCRetainAutorelease(QualType type, + llvm::Value *value) { + if (!type->isBlockPointerType()) + return EmitARCRetainAutoreleaseNonBlock(value); + + if (isa<llvm::ConstantPointerNull>(value)) return value; + + const llvm::Type *origType = value->getType(); + value = Builder.CreateBitCast(value, Int8PtrTy); + value = EmitARCRetainBlock(value); + value = EmitARCAutorelease(value); + return Builder.CreateBitCast(value, origType); +} + +/// Do a fused retain/autorelease of the given object. +/// call i8* @objc_retainAutorelease(i8* %value) +llvm::Value * +CodeGenFunction::EmitARCRetainAutoreleaseNonBlock(llvm::Value *value) { + return emitARCValueOperation(*this, value, + CGM.getARCEntrypoints().objc_retainAutorelease, + "objc_retainAutorelease"); +} + +/// i8* @objc_loadWeak(i8** %addr) +/// Essentially objc_autorelease(objc_loadWeakRetained(addr)). +llvm::Value *CodeGenFunction::EmitARCLoadWeak(llvm::Value *addr) { + return emitARCLoadOperation(*this, addr, + CGM.getARCEntrypoints().objc_loadWeak, + "objc_loadWeak"); +} + +/// i8* @objc_loadWeakRetained(i8** %addr) +llvm::Value *CodeGenFunction::EmitARCLoadWeakRetained(llvm::Value *addr) { + return emitARCLoadOperation(*this, addr, + CGM.getARCEntrypoints().objc_loadWeakRetained, + "objc_loadWeakRetained"); +} + +/// i8* @objc_storeWeak(i8** %addr, i8* %value) +/// Returns %value. +llvm::Value *CodeGenFunction::EmitARCStoreWeak(llvm::Value *addr, + llvm::Value *value, + bool ignored) { + return emitARCStoreOperation(*this, addr, value, + CGM.getARCEntrypoints().objc_storeWeak, + "objc_storeWeak", ignored); +} + +/// i8* @objc_initWeak(i8** %addr, i8* %value) +/// Returns %value. %addr is known to not have a current weak entry. +/// Essentially equivalent to: +/// *addr = nil; objc_storeWeak(addr, value); +void CodeGenFunction::EmitARCInitWeak(llvm::Value *addr, llvm::Value *value) { + // If we're initializing to null, just write null to memory; no need + // to get the runtime involved. But don't do this if optimization + // is enabled, because accounting for this would make the optimizer + // much more complicated. + if (isa<llvm::ConstantPointerNull>(value) && + CGM.getCodeGenOpts().OptimizationLevel == 0) { + Builder.CreateStore(value, addr); + return; + } + + emitARCStoreOperation(*this, addr, value, + CGM.getARCEntrypoints().objc_initWeak, + "objc_initWeak", /*ignored*/ true); +} + +/// void @objc_destroyWeak(i8** %addr) +/// Essentially objc_storeWeak(addr, nil). +void CodeGenFunction::EmitARCDestroyWeak(llvm::Value *addr) { + llvm::Constant *&fn = CGM.getARCEntrypoints().objc_destroyWeak; + if (!fn) { + std::vector<const llvm::Type*> args(1, Int8PtrPtrTy); + const llvm::FunctionType *fnType = + llvm::FunctionType::get(Builder.getVoidTy(), args, false); + fn = createARCRuntimeFunction(CGM, fnType, "objc_destroyWeak"); + } + + // Cast the argument to 'id*'. + addr = Builder.CreateBitCast(addr, Int8PtrPtrTy); + + llvm::CallInst *call = Builder.CreateCall(fn, addr); + call->setDoesNotThrow(); +} + +/// void @objc_moveWeak(i8** %dest, i8** %src) +/// Disregards the current value in %dest. Leaves %src pointing to nothing. +/// Essentially (objc_copyWeak(dest, src), objc_destroyWeak(src)). +void CodeGenFunction::EmitARCMoveWeak(llvm::Value *dst, llvm::Value *src) { + emitARCCopyOperation(*this, dst, src, + CGM.getARCEntrypoints().objc_moveWeak, + "objc_moveWeak"); +} + +/// void @objc_copyWeak(i8** %dest, i8** %src) +/// Disregards the current value in %dest. Essentially +/// objc_release(objc_initWeak(dest, objc_readWeakRetained(src))) +void CodeGenFunction::EmitARCCopyWeak(llvm::Value *dst, llvm::Value *src) { + emitARCCopyOperation(*this, dst, src, + CGM.getARCEntrypoints().objc_copyWeak, + "objc_copyWeak"); +} + +/// Produce the code to do a objc_autoreleasepool_push. +/// call i8* @objc_autoreleasePoolPush(void) +llvm::Value *CodeGenFunction::EmitObjCAutoreleasePoolPush() { + llvm::Constant *&fn = CGM.getRREntrypoints().objc_autoreleasePoolPush; + if (!fn) { + const llvm::FunctionType *fnType = + llvm::FunctionType::get(Int8PtrTy, false); + fn = createARCRuntimeFunction(CGM, fnType, "objc_autoreleasePoolPush"); + } + + llvm::CallInst *call = Builder.CreateCall(fn); + call->setDoesNotThrow(); + + return call; +} + +/// Produce the code to do a primitive release. +/// call void @objc_autoreleasePoolPop(i8* %ptr) +void CodeGenFunction::EmitObjCAutoreleasePoolPop(llvm::Value *value) { + assert(value->getType() == Int8PtrTy); + + llvm::Constant *&fn = CGM.getRREntrypoints().objc_autoreleasePoolPop; + if (!fn) { + std::vector<const llvm::Type*> args(1, Int8PtrTy); + const llvm::FunctionType *fnType = + llvm::FunctionType::get(Builder.getVoidTy(), args, false); + + // We don't want to use a weak import here; instead we should not + // fall into this path. + fn = createARCRuntimeFunction(CGM, fnType, "objc_autoreleasePoolPop"); + } + + llvm::CallInst *call = Builder.CreateCall(fn, value); + call->setDoesNotThrow(); +} + +/// Produce the code to do an MRR version objc_autoreleasepool_push. +/// Which is: [[NSAutoreleasePool alloc] init]; +/// Where alloc is declared as: + (id) alloc; in NSAutoreleasePool class. +/// init is declared as: - (id) init; in its NSObject super class. +/// +llvm::Value *CodeGenFunction::EmitObjCMRRAutoreleasePoolPush() { + CGObjCRuntime &Runtime = CGM.getObjCRuntime(); + llvm::Value *Receiver = Runtime.EmitNSAutoreleasePoolClassRef(Builder); + // [NSAutoreleasePool alloc] + IdentifierInfo *II = &CGM.getContext().Idents.get("alloc"); + Selector AllocSel = getContext().Selectors.getSelector(0, &II); + CallArgList Args; + RValue AllocRV = + Runtime.GenerateMessageSend(*this, ReturnValueSlot(), + getContext().getObjCIdType(), + AllocSel, Receiver, Args); + + // [Receiver init] + Receiver = AllocRV.getScalarVal(); + II = &CGM.getContext().Idents.get("init"); + Selector InitSel = getContext().Selectors.getSelector(0, &II); + RValue InitRV = + Runtime.GenerateMessageSend(*this, ReturnValueSlot(), + getContext().getObjCIdType(), + InitSel, Receiver, Args); + return InitRV.getScalarVal(); +} + +/// Produce the code to do a primitive release. +/// [tmp drain]; +void CodeGenFunction::EmitObjCMRRAutoreleasePoolPop(llvm::Value *Arg) { + IdentifierInfo *II = &CGM.getContext().Idents.get("drain"); + Selector DrainSel = getContext().Selectors.getSelector(0, &II); + CallArgList Args; + CGM.getObjCRuntime().GenerateMessageSend(*this, ReturnValueSlot(), + getContext().VoidTy, DrainSel, Arg, Args); +} + +namespace { + struct ObjCReleasingCleanup : EHScopeStack::Cleanup { + private: + QualType type; + llvm::Value *addr; + + protected: + ObjCReleasingCleanup(QualType type, llvm::Value *addr) + : type(type), addr(addr) {} + + virtual llvm::Value *getAddress(CodeGenFunction &CGF, + llvm::Value *addr) { + return addr; + } + + virtual void release(CodeGenFunction &CGF, + QualType type, + llvm::Value *addr) = 0; + + public: + void Emit(CodeGenFunction &CGF, bool isForEH) { + const ArrayType *arrayType = CGF.getContext().getAsArrayType(type); + + llvm::Value *addr = getAddress(CGF, this->addr); + + // If we don't have an array type, this is easy. + if (!arrayType) + return release(CGF, type, addr); + + llvm::Value *begin = addr; + QualType baseType; + + // Otherwise, this is more painful. + llvm::Value *count = emitArrayLength(CGF, arrayType, baseType, + begin); + + assert(baseType == CGF.getContext().getBaseElementType(arrayType)); + + llvm::BasicBlock *incomingBB = CGF.Builder.GetInsertBlock(); + + // id *cur = begin; + // id *end = begin + count; + llvm::Value *end = + CGF.Builder.CreateInBoundsGEP(begin, count, "array.end"); + + // loopBB: + llvm::BasicBlock *loopBB = CGF.createBasicBlock("release-loop"); + CGF.EmitBlock(loopBB); + + llvm::PHINode *cur = CGF.Builder.CreatePHI(begin->getType(), 2, "cur"); + cur->addIncoming(begin, incomingBB); + + // if (cur == end) goto endBB; + llvm::Value *eq = CGF.Builder.CreateICmpEQ(cur, end, "release-loop.done"); + llvm::BasicBlock *bodyBB = CGF.createBasicBlock("release-loop.body"); + llvm::BasicBlock *endBB = CGF.createBasicBlock("release-loop.cont"); + CGF.Builder.CreateCondBr(eq, endBB, bodyBB); + CGF.EmitBlock(bodyBB); + + // Release the value at 'cur'. + release(CGF, baseType, cur); + + // ++cur; + // goto loopBB; + llvm::Value *next = CGF.Builder.CreateConstInBoundsGEP1_32(cur, 1); + cur->addIncoming(next, CGF.Builder.GetInsertBlock()); + CGF.Builder.CreateBr(loopBB); + + // endBB: + CGF.EmitBlock(endBB); + } + + private: + /// Computes the length of an array in elements, as well + /// as the base + static llvm::Value *emitArrayLength(CodeGenFunction &CGF, + const ArrayType *origArrayType, + QualType &baseType, + llvm::Value *&addr) { + ASTContext &Ctx = CGF.getContext(); + const ArrayType *arrayType = origArrayType; + + // If it's a VLA, we have to load the stored size. Note that + // this is the size of the VLA in bytes, not its size in elements. + llvm::Value *vlaSizeInBytes = 0; + if (isa<VariableArrayType>(arrayType)) { + vlaSizeInBytes = CGF.GetVLASize(cast<VariableArrayType>(arrayType)); + + // Walk into all VLAs. This doesn't require changes to addr, + // which has type T* where T is the first non-VLA element type. + do { + QualType elementType = arrayType->getElementType(); + arrayType = Ctx.getAsArrayType(elementType); + + // If we only have VLA components, 'addr' requires no adjustment. + if (!arrayType) { + baseType = elementType; + return divideVLASizeByBaseType(CGF, vlaSizeInBytes, baseType); + } + } while (isa<VariableArrayType>(arrayType)); + + // We get out here only if we find a constant array type + // inside the VLA. + } + + // We have some number of constant-length arrays, so addr should + // have LLVM type [M x [N x [...]]]*. Build a GEP that walks + // down to the first element of addr. + llvm::SmallVector<llvm::Value*, 8> gepIndices; + + // GEP down to the array type. + llvm::ConstantInt *zero = CGF.Builder.getInt32(0); + gepIndices.push_back(zero); + + // It's more efficient to calculate the count from the LLVM + // constant-length arrays than to re-evaluate the array bounds. + uint64_t countFromCLAs = 1; + + const llvm::ArrayType *llvmArrayType = + cast<llvm::ArrayType>( + cast<llvm::PointerType>(addr->getType())->getElementType()); + while (true) { + assert(isa<ConstantArrayType>(arrayType)); + assert(cast<ConstantArrayType>(arrayType)->getSize().getZExtValue() + == llvmArrayType->getNumElements()); + + gepIndices.push_back(zero); + countFromCLAs *= llvmArrayType->getNumElements(); + + llvmArrayType = + dyn_cast<llvm::ArrayType>(llvmArrayType->getElementType()); + if (!llvmArrayType) break; + + arrayType = Ctx.getAsArrayType(arrayType->getElementType()); + assert(arrayType && "LLVM and Clang types are out-of-synch"); + } + + // Create the actual GEP. + addr = CGF.Builder.CreateInBoundsGEP(addr, gepIndices.begin(), + gepIndices.end(), "array.begin"); + + baseType = arrayType->getElementType(); + + // If we had an VLA dimensions, we need to use the captured size. + if (vlaSizeInBytes) + return divideVLASizeByBaseType(CGF, vlaSizeInBytes, baseType); + + // Otherwise, use countFromCLAs. + assert(countFromCLAs == (uint64_t) + (Ctx.getTypeSizeInChars(origArrayType).getQuantity() / + Ctx.getTypeSizeInChars(baseType).getQuantity())); + + return llvm::ConstantInt::get(CGF.IntPtrTy, countFromCLAs); + } + + static llvm::Value *divideVLASizeByBaseType(CodeGenFunction &CGF, + llvm::Value *vlaSizeInBytes, + QualType baseType) { + // Divide the base type size back out of the + CharUnits baseSize = CGF.getContext().getTypeSizeInChars(baseType); + llvm::Value *baseSizeInBytes = + llvm::ConstantInt::get(vlaSizeInBytes->getType(), + baseSize.getQuantity()); + + return CGF.Builder.CreateUDiv(vlaSizeInBytes, baseSizeInBytes, + "array.vla-count"); + } + }; + + /// A cleanup that calls @objc_release on all the objects to release. + struct CallReleaseForObject : ObjCReleasingCleanup { + bool precise; + CallReleaseForObject(QualType type, llvm::Value *addr, bool precise) + : ObjCReleasingCleanup(type, addr), precise(precise) {} + + void release(CodeGenFunction &CGF, QualType type, llvm::Value *addr) { + llvm::Value *ptr = CGF.Builder.CreateLoad(addr, "tmp"); + CGF.EmitARCRelease(ptr, precise); + } + }; + + /// A cleanup that calls @objc_storeStrong(nil) on all the objects to + /// release in an ivar. + struct CallReleaseForIvar : ObjCReleasingCleanup { + const ObjCIvarDecl *ivar; + CallReleaseForIvar(const ObjCIvarDecl *ivar, llvm::Value *self) + : ObjCReleasingCleanup(ivar->getType(), self), ivar(ivar) {} + + llvm::Value *getAddress(CodeGenFunction &CGF, llvm::Value *addr) { + LValue lvalue + = CGF.EmitLValueForIvar(CGF.TypeOfSelfObject(), addr, ivar, /*CVR*/ 0); + return lvalue.getAddress(); + } + + void release(CodeGenFunction &CGF, QualType type, llvm::Value *addr) { + // Release ivars by storing nil into them; it just makes things easier. + llvm::Value *null = getNullForVariable(addr); + CGF.EmitARCStoreStrongCall(addr, null, /*ignored*/ true); + } + }; + + /// A cleanup that calls @objc_release on all of the objects to release in + /// a field. + struct CallReleaseForField : CallReleaseForObject { + const FieldDecl *Field; + + explicit CallReleaseForField(const FieldDecl *Field) + : CallReleaseForObject(Field->getType(), 0, /*precise=*/true), + Field(Field) { } + + llvm::Value *getAddress(CodeGenFunction &CGF, llvm::Value *) { + llvm::Value *This = CGF.LoadCXXThis(); + LValue LV = CGF.EmitLValueForField(This, Field, 0); + return LV.getAddress(); + } + }; + + /// A cleanup that calls @objc_weak_release on all the objects to + /// release in an object. + struct CallWeakReleaseForObject : ObjCReleasingCleanup { + CallWeakReleaseForObject(QualType type, llvm::Value *addr) + : ObjCReleasingCleanup(type, addr) {} + + void release(CodeGenFunction &CGF, QualType type, llvm::Value *addr) { + CGF.EmitARCDestroyWeak(addr); + } + }; + + + /// A cleanup that calls @objc_weak_release on all the objects to + /// release in an ivar. + struct CallWeakReleaseForIvar : CallWeakReleaseForObject { + const ObjCIvarDecl *ivar; + CallWeakReleaseForIvar(const ObjCIvarDecl *ivar, llvm::Value *self) + : CallWeakReleaseForObject(ivar->getType(), self), ivar(ivar) {} + + llvm::Value *getAddress(CodeGenFunction &CGF, llvm::Value *addr) { + LValue lvalue + = CGF.EmitLValueForIvar(CGF.TypeOfSelfObject(), addr, ivar, /*CVR*/ 0); + return lvalue.getAddress(); + } + }; + + /// A cleanup that calls @objc_weak_release on all the objects to + /// release in a field; + struct CallWeakReleaseForField : CallWeakReleaseForObject { + const FieldDecl *Field; + CallWeakReleaseForField(const FieldDecl *Field) + : CallWeakReleaseForObject(Field->getType(), 0), Field(Field) {} + + llvm::Value *getAddress(CodeGenFunction &CGF, llvm::Value *) { + llvm::Value *This = CGF.LoadCXXThis(); + LValue LV = CGF.EmitLValueForField(This, Field, 0); + return LV.getAddress(); + } + }; + + struct CallObjCAutoreleasePoolObject : EHScopeStack::Cleanup { + llvm::Value *Token; + + CallObjCAutoreleasePoolObject(llvm::Value *token) : Token(token) {} + + void Emit(CodeGenFunction &CGF, bool isForEH) { + CGF.EmitObjCAutoreleasePoolPop(Token); + } + }; + struct CallObjCMRRAutoreleasePoolObject : EHScopeStack::Cleanup { + llvm::Value *Token; + + CallObjCMRRAutoreleasePoolObject(llvm::Value *token) : Token(token) {} + + void Emit(CodeGenFunction &CGF, bool isForEH) { + CGF.EmitObjCMRRAutoreleasePoolPop(Token); + } + }; +} + +void CodeGenFunction::EmitObjCAutoreleasePoolCleanup(llvm::Value *Ptr) { + if (CGM.getLangOptions().ObjCAutoRefCount) + EHStack.pushCleanup<CallObjCAutoreleasePoolObject>(NormalCleanup, Ptr); + else + EHStack.pushCleanup<CallObjCMRRAutoreleasePoolObject>(NormalCleanup, Ptr); +} + +/// PushARCReleaseCleanup - Enter a cleanup to perform a release on a +/// given object or array of objects. +void CodeGenFunction::PushARCReleaseCleanup(CleanupKind cleanupKind, + QualType type, + llvm::Value *addr, + bool precise) { + EHStack.pushCleanup<CallReleaseForObject>(cleanupKind, type, addr, precise); +} + +/// PushARCWeakReleaseCleanup - Enter a cleanup to perform a weak +/// release on the given object or array of objects. +void CodeGenFunction::PushARCWeakReleaseCleanup(CleanupKind cleanupKind, + QualType type, + llvm::Value *addr) { + EHStack.pushCleanup<CallWeakReleaseForObject>(cleanupKind, type, addr); +} + +/// PushARCReleaseCleanup - Enter a cleanup to perform a release on a +/// given object or array of objects. +void CodeGenFunction::PushARCFieldReleaseCleanup(CleanupKind cleanupKind, + const FieldDecl *field) { + EHStack.pushCleanup<CallReleaseForField>(cleanupKind, field); +} + +/// PushARCWeakReleaseCleanup - Enter a cleanup to perform a weak +/// release on the given object or array of objects. +void CodeGenFunction::PushARCFieldWeakReleaseCleanup(CleanupKind cleanupKind, + const FieldDecl *field) { + EHStack.pushCleanup<CallWeakReleaseForField>(cleanupKind, field); +} + +static void pushReleaseForIvar(CodeGenFunction &CGF, ObjCIvarDecl *ivar, + llvm::Value *self) { + CGF.EHStack.pushCleanup<CallReleaseForIvar>(CGF.getARCCleanupKind(), + ivar, self); +} + +static void pushWeakReleaseForIvar(CodeGenFunction &CGF, ObjCIvarDecl *ivar, + llvm::Value *self) { + CGF.EHStack.pushCleanup<CallWeakReleaseForIvar>(CGF.getARCCleanupKind(), + ivar, self); +} + +static TryEmitResult tryEmitARCRetainLoadOfScalar(CodeGenFunction &CGF, + LValue lvalue, + QualType type) { + switch (type.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Autoreleasing: + return TryEmitResult(CGF.EmitLoadOfLValue(lvalue, type).getScalarVal(), + false); + + case Qualifiers::OCL_Weak: + return TryEmitResult(CGF.EmitARCLoadWeakRetained(lvalue.getAddress()), + true); + } + + llvm_unreachable("impossible lifetime!"); + return TryEmitResult(); +} + +static TryEmitResult tryEmitARCRetainLoadOfScalar(CodeGenFunction &CGF, + const Expr *e) { + e = e->IgnoreParens(); + QualType type = e->getType(); + + // As a very special optimization, in ARC++, if the l-value is the + // result of a non-volatile assignment, do a simple retain of the + // result of the call to objc_storeWeak instead of reloading. + if (CGF.getLangOptions().CPlusPlus && + !type.isVolatileQualified() && + type.getObjCLifetime() == Qualifiers::OCL_Weak && + isa<BinaryOperator>(e) && + cast<BinaryOperator>(e)->getOpcode() == BO_Assign) + return TryEmitResult(CGF.EmitScalarExpr(e), false); + + return tryEmitARCRetainLoadOfScalar(CGF, CGF.EmitLValue(e), type); +} + +static llvm::Value *emitARCRetainAfterCall(CodeGenFunction &CGF, + llvm::Value *value); + +/// Given that the given expression is some sort of call (which does +/// not return retained), emit a retain following it. +static llvm::Value *emitARCRetainCall(CodeGenFunction &CGF, const Expr *e) { + llvm::Value *value = CGF.EmitScalarExpr(e); + return emitARCRetainAfterCall(CGF, value); +} + +static llvm::Value *emitARCRetainAfterCall(CodeGenFunction &CGF, + llvm::Value *value) { + if (llvm::CallInst *call = dyn_cast<llvm::CallInst>(value)) { + CGBuilderTy::InsertPoint ip = CGF.Builder.saveIP(); + + // Place the retain immediately following the call. + CGF.Builder.SetInsertPoint(call->getParent(), + ++llvm::BasicBlock::iterator(call)); + value = CGF.EmitARCRetainAutoreleasedReturnValue(value); + + CGF.Builder.restoreIP(ip); + return value; + } else if (llvm::InvokeInst *invoke = dyn_cast<llvm::InvokeInst>(value)) { + CGBuilderTy::InsertPoint ip = CGF.Builder.saveIP(); + + // Place the retain at the beginning of the normal destination block. + llvm::BasicBlock *BB = invoke->getNormalDest(); + CGF.Builder.SetInsertPoint(BB, BB->begin()); + value = CGF.EmitARCRetainAutoreleasedReturnValue(value); + + CGF.Builder.restoreIP(ip); + return value; + + // Bitcasts can arise because of related-result returns. Rewrite + // the operand. + } else if (llvm::BitCastInst *bitcast = dyn_cast<llvm::BitCastInst>(value)) { + llvm::Value *operand = bitcast->getOperand(0); + operand = emitARCRetainAfterCall(CGF, operand); + bitcast->setOperand(0, operand); + return bitcast; + + // Generic fall-back case. + } else { + // Retain using the non-block variant: we never need to do a copy + // of a block that's been returned to us. + return CGF.EmitARCRetainNonBlock(value); + } +} + +static TryEmitResult +tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e) { + QualType originalType = e->getType(); + + // The desired result type, if it differs from the type of the + // ultimate opaque expression. + const llvm::Type *resultType = 0; + + while (true) { + e = e->IgnoreParens(); + + // There's a break at the end of this if-chain; anything + // that wants to keep looping has to explicitly continue. + if (const CastExpr *ce = dyn_cast<CastExpr>(e)) { + switch (ce->getCastKind()) { + // No-op casts don't change the type, so we just ignore them. + case CK_NoOp: + e = ce->getSubExpr(); + continue; + + case CK_LValueToRValue: { + TryEmitResult loadResult + = tryEmitARCRetainLoadOfScalar(CGF, ce->getSubExpr()); + if (resultType) { + llvm::Value *value = loadResult.getPointer(); + value = CGF.Builder.CreateBitCast(value, resultType); + loadResult.setPointer(value); + } + return loadResult; + } + + // These casts can change the type, so remember that and + // soldier on. We only need to remember the outermost such + // cast, though. + case CK_AnyPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_BitCast: + if (!resultType) + resultType = CGF.ConvertType(ce->getType()); + e = ce->getSubExpr(); + assert(e->getType()->hasPointerRepresentation()); + continue; + + // For consumptions, just emit the subexpression and thus elide + // the retain/release pair. + case CK_ObjCConsumeObject: { + llvm::Value *result = CGF.EmitScalarExpr(ce->getSubExpr()); + if (resultType) result = CGF.Builder.CreateBitCast(result, resultType); + return TryEmitResult(result, true); + } + + case CK_GetObjCProperty: { + llvm::Value *result = emitARCRetainCall(CGF, ce); + if (resultType) result = CGF.Builder.CreateBitCast(result, resultType); + return TryEmitResult(result, true); + } + + default: + break; + } + + // Skip __extension__. + } else if (const UnaryOperator *op = dyn_cast<UnaryOperator>(e)) { + if (op->getOpcode() == UO_Extension) { + e = op->getSubExpr(); + continue; + } + + // For calls and message sends, use the retained-call logic. + // Delegate inits are a special case in that they're the only + // returns-retained expression that *isn't* surrounded by + // a consume. + } else if (isa<CallExpr>(e) || + (isa<ObjCMessageExpr>(e) && + !cast<ObjCMessageExpr>(e)->isDelegateInitCall())) { + llvm::Value *result = emitARCRetainCall(CGF, e); + if (resultType) result = CGF.Builder.CreateBitCast(result, resultType); + return TryEmitResult(result, true); + } + + // Conservatively halt the search at any other expression kind. + break; + } + + // We didn't find an obvious production, so emit what we've got and + // tell the caller that we didn't manage to retain. + llvm::Value *result = CGF.EmitScalarExpr(e); + if (resultType) result = CGF.Builder.CreateBitCast(result, resultType); + return TryEmitResult(result, false); +} + +static llvm::Value *emitARCRetainLoadOfScalar(CodeGenFunction &CGF, + LValue lvalue, + QualType type) { + TryEmitResult result = tryEmitARCRetainLoadOfScalar(CGF, lvalue, type); + llvm::Value *value = result.getPointer(); + if (!result.getInt()) + value = CGF.EmitARCRetain(type, value); + return value; +} + +/// EmitARCRetainScalarExpr - Semantically equivalent to +/// EmitARCRetainObject(e->getType(), EmitScalarExpr(e)), but making a +/// best-effort attempt to peephole expressions that naturally produce +/// retained objects. +llvm::Value *CodeGenFunction::EmitARCRetainScalarExpr(const Expr *e) { + TryEmitResult result = tryEmitARCRetainScalarExpr(*this, e); + llvm::Value *value = result.getPointer(); + if (!result.getInt()) + value = EmitARCRetain(e->getType(), value); + return value; +} + +llvm::Value * +CodeGenFunction::EmitARCRetainAutoreleaseScalarExpr(const Expr *e) { + TryEmitResult result = tryEmitARCRetainScalarExpr(*this, e); + llvm::Value *value = result.getPointer(); + if (result.getInt()) + value = EmitARCAutorelease(value); + else + value = EmitARCRetainAutorelease(e->getType(), value); + return value; +} + +std::pair<LValue,llvm::Value*> +CodeGenFunction::EmitARCStoreStrong(const BinaryOperator *e, + bool ignored) { + // Evaluate the RHS first. + TryEmitResult result = tryEmitARCRetainScalarExpr(*this, e->getRHS()); + llvm::Value *value = result.getPointer(); + + LValue lvalue = EmitLValue(e->getLHS()); + + // If the RHS was emitted retained, expand this. + if (result.getInt()) { + llvm::Value *oldValue = + EmitLoadOfScalar(lvalue.getAddress(), lvalue.isVolatileQualified(), + lvalue.getAlignment(), e->getType(), + lvalue.getTBAAInfo()); + EmitStoreOfScalar(value, lvalue.getAddress(), + lvalue.isVolatileQualified(), lvalue.getAlignment(), + e->getType(), lvalue.getTBAAInfo()); + EmitARCRelease(oldValue, /*precise*/ false); + } else { + value = EmitARCStoreStrong(lvalue, e->getType(), value, ignored); + } + + return std::pair<LValue,llvm::Value*>(lvalue, value); +} + +std::pair<LValue,llvm::Value*> +CodeGenFunction::EmitARCStoreAutoreleasing(const BinaryOperator *e) { + llvm::Value *value = EmitARCRetainAutoreleaseScalarExpr(e->getRHS()); + LValue lvalue = EmitLValue(e->getLHS()); + + EmitStoreOfScalar(value, lvalue.getAddress(), + lvalue.isVolatileQualified(), lvalue.getAlignment(), + e->getType(), lvalue.getTBAAInfo()); + + return std::pair<LValue,llvm::Value*>(lvalue, value); +} + +void CodeGenFunction::EmitObjCAutoreleasePoolStmt( + const ObjCAutoreleasePoolStmt &ARPS) { + const Stmt *subStmt = ARPS.getSubStmt(); + const CompoundStmt &S = cast<CompoundStmt>(*subStmt); + + CGDebugInfo *DI = getDebugInfo(); + if (DI) { + DI->setLocation(S.getLBracLoc()); + DI->EmitRegionStart(Builder); + } + + // Keep track of the current cleanup stack depth. + RunCleanupsScope Scope(*this); + const llvm::Triple Triple = getContext().Target.getTriple(); + if (CGM.getLangOptions().ObjCAutoRefCount || + (CGM.isTargetDarwin() && + ((Triple.getArch() == llvm::Triple::x86_64 && + Triple.getDarwinMajorNumber() >= 11) + || (Triple.getEnvironmentName() == "iphoneos" && + Triple.getDarwinMajorNumber() >= 5)))) { + llvm::Value *token = EmitObjCAutoreleasePoolPush(); + EHStack.pushCleanup<CallObjCAutoreleasePoolObject>(NormalCleanup, token); + } else { + llvm::Value *token = EmitObjCMRRAutoreleasePoolPush(); + EHStack.pushCleanup<CallObjCMRRAutoreleasePoolObject>(NormalCleanup, token); + } + + for (CompoundStmt::const_body_iterator I = S.body_begin(), + E = S.body_end(); I != E; ++I) + EmitStmt(*I); + + if (DI) { + DI->setLocation(S.getRBracLoc()); + DI->EmitRegionEnd(Builder); + } +} CGObjCRuntime::~CGObjCRuntime() {} diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp index f0993c5dadb..e85c8980fef 100644 --- a/clang/lib/CodeGen/CGObjCGNU.cpp +++ b/clang/lib/CodeGen/CGObjCGNU.cpp @@ -969,7 +969,7 @@ CGObjCGNU::GenerateMessageSendSuper(CodeGenFunction &CGF, ActualArgs.add(RValue::get(EnforceType(Builder, Receiver, IdTy)), ASTIdTy); ActualArgs.add(RValue::get(cmd), CGF.getContext().getObjCSelType()); - ActualArgs.insert(ActualArgs.end(), CallArgs.begin(), CallArgs.end()); + ActualArgs.addFrom(CallArgs); CodeGenTypes &Types = CGM.getTypes(); const CGFunctionInfo &FnInfo = Types.getFunctionInfo(ResultType, ActualArgs, @@ -1121,7 +1121,7 @@ CGObjCGNU::GenerateMessageSend(CodeGenFunction &CGF, CallArgList ActualArgs; ActualArgs.add(RValue::get(Receiver), ASTIdTy); ActualArgs.add(RValue::get(cmd), CGF.getContext().getObjCSelType()); - ActualArgs.insert(ActualArgs.end(), CallArgs.begin(), CallArgs.end()); + ActualArgs.addFrom(CallArgs); CodeGenTypes &Types = CGM.getTypes(); const CGFunctionInfo &FnInfo = Types.getFunctionInfo(ResultType, ActualArgs, diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 8c3e9a36e30..988b16e945a 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -63,10 +63,13 @@ private: /// The default messenger, used for sends whose ABI is unchanged from /// the all-integer/pointer case. llvm::Constant *getMessageSendFn() const { + // Add the non-lazy-bind attribute, since objc_msgSend is likely to + // be called a lot. const llvm::Type *params[] = { ObjectPtrTy, SelectorPtrTy }; return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, params, true), - "objc_msgSend"); + "objc_msgSend", + llvm::Attribute::NonLazyBind); } /// void objc_msgSend_stret (id, SEL, ...) @@ -887,6 +890,11 @@ private: llvm::Value *EmitClassRef(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID); + llvm::Value *EmitClassRefFromId(CGBuilderTy &Builder, + IdentifierInfo *II); + + llvm::Value *EmitNSAutoreleasePoolClassRef(CGBuilderTy &Builder); + /// EmitSuperClassRef - Emits reference to class's main metadata class. llvm::Value *EmitSuperClassRef(const ObjCInterfaceDecl *ID); @@ -1158,6 +1166,11 @@ private: /// for the given class reference. llvm::Value *EmitClassRef(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID); + + llvm::Value *EmitClassRefFromId(CGBuilderTy &Builder, + IdentifierInfo *II); + + llvm::Value *EmitNSAutoreleasePoolClassRef(CGBuilderTy &Builder); /// EmitSuperClassRef - Return a Value*, of type ObjCTypes.ClassPtrTy, /// for the given super class reference. @@ -1526,7 +1539,7 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF, Arg0 = CGF.Builder.CreateBitCast(Arg0, ObjCTypes.ObjectPtrTy, "tmp"); ActualArgs.add(RValue::get(Arg0), Arg0Ty); ActualArgs.add(RValue::get(Sel), CGF.getContext().getObjCSelType()); - ActualArgs.insert(ActualArgs.end(), CallArgs.begin(), CallArgs.end()); + ActualArgs.addFrom(CallArgs); CodeGenTypes &Types = CGM.getTypes(); const CGFunctionInfo &FnInfo = Types.getFunctionInfo(ResultType, ActualArgs, @@ -1562,7 +1575,7 @@ static Qualifiers::GC GetGCAttrTypeForType(ASTContext &Ctx, QualType FQT) { if (FQT.isObjCGCStrong()) return Qualifiers::Strong; - if (FQT.isObjCGCWeak()) + if (FQT.isObjCGCWeak() || FQT.getObjCLifetime() == Qualifiers::OCL_Weak) return Qualifiers::Weak; if (FQT->isObjCObjectPointerType() || FQT->isBlockPointerType()) @@ -1579,7 +1592,8 @@ llvm::Constant *CGObjCCommonMac::BuildGCBlockLayout(CodeGenModule &CGM, llvm::Constant *nullPtr = llvm::Constant::getNullValue(llvm::Type::getInt8PtrTy(VMContext)); - if (CGM.getLangOptions().getGCMode() == LangOptions::NonGC) + if (CGM.getLangOptions().getGCMode() == LangOptions::NonGC && + !CGM.getLangOptions().ObjCAutoRefCount) return nullPtr; bool hasUnion = false; @@ -2129,7 +2143,7 @@ void CGObjCMac::GenerateClass(const ObjCImplementationDecl *ID) { Interface->all_referenced_protocol_begin(), Interface->all_referenced_protocol_end()); unsigned Flags = eClassFlags_Factory; - if (ID->getNumIvarInitializers()) + if (ID->hasCXXStructors()) Flags |= eClassFlags_HasCXXStructors; unsigned Size = CGM.getContext().getASTObjCImplementationLayout(ID).getSize().getQuantity(); @@ -3461,25 +3475,35 @@ llvm::Constant *CGObjCMac::EmitModuleSymbols() { return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.SymtabPtrTy); } -llvm::Value *CGObjCMac::EmitClassRef(CGBuilderTy &Builder, - const ObjCInterfaceDecl *ID) { - LazySymbols.insert(ID->getIdentifier()); - - llvm::GlobalVariable *&Entry = ClassReferences[ID->getIdentifier()]; - +llvm::Value *CGObjCMac::EmitClassRefFromId(CGBuilderTy &Builder, + IdentifierInfo *II) { + LazySymbols.insert(II); + + llvm::GlobalVariable *&Entry = ClassReferences[II]; + if (!Entry) { llvm::Constant *Casted = - llvm::ConstantExpr::getBitCast(GetClassName(ID->getIdentifier()), - ObjCTypes.ClassPtrTy); + llvm::ConstantExpr::getBitCast(GetClassName(II), + ObjCTypes.ClassPtrTy); Entry = - CreateMetadataVar("\01L_OBJC_CLASS_REFERENCES_", Casted, - "__OBJC,__cls_refs,literal_pointers,no_dead_strip", - 4, true); + CreateMetadataVar("\01L_OBJC_CLASS_REFERENCES_", Casted, + "__OBJC,__cls_refs,literal_pointers,no_dead_strip", + 4, true); } - + return Builder.CreateLoad(Entry, "tmp"); } +llvm::Value *CGObjCMac::EmitClassRef(CGBuilderTy &Builder, + const ObjCInterfaceDecl *ID) { + return EmitClassRefFromId(Builder, ID->getIdentifier()); +} + +llvm::Value *CGObjCMac::EmitNSAutoreleasePoolClassRef(CGBuilderTy &Builder) { + IdentifierInfo *II = &CGM.getContext().Idents.get("NSAutoreleasePool"); + return EmitClassRefFromId(Builder, II); +} + llvm::Value *CGObjCMac::EmitSelector(CGBuilderTy &Builder, Selector Sel, bool lvalue) { llvm::GlobalVariable *&Entry = SelectorReferences[Sel]; @@ -3567,12 +3591,18 @@ void CGObjCCommonMac::BuildAggrIvarLayout(const ObjCImplementationDecl *OI, uint64_t MaxFieldOffset = 0; uint64_t MaxSkippedFieldOffset = 0; uint64_t LastBitfieldOrUnnamedOffset = 0; + uint64_t FirstFieldDelta = 0; if (RecFields.empty()) return; unsigned WordSizeInBits = CGM.getContext().Target.getPointerWidth(0); unsigned ByteSizeInBits = CGM.getContext().Target.getCharWidth(); - + if (!RD && CGM.getLangOptions().ObjCAutoRefCount) { + FieldDecl *FirstField = RecFields[0]; + FirstFieldDelta = + ComputeIvarBaseOffset(CGM, OI, cast<ObjCIvarDecl>(FirstField)); + } + for (unsigned i = 0, e = RecFields.size(); i != e; ++i) { FieldDecl *Field = RecFields[i]; uint64_t FieldOffset; @@ -3580,9 +3610,10 @@ void CGObjCCommonMac::BuildAggrIvarLayout(const ObjCImplementationDecl *OI, // Note that 'i' here is actually the field index inside RD of Field, // although this dependency is hidden. const ASTRecordLayout &RL = CGM.getContext().getASTRecordLayout(RD); - FieldOffset = RL.getFieldOffset(i) / ByteSizeInBits; + FieldOffset = (RL.getFieldOffset(i) / ByteSizeInBits) - FirstFieldDelta; } else - FieldOffset = ComputeIvarBaseOffset(CGM, OI, cast<ObjCIvarDecl>(Field)); + FieldOffset = + ComputeIvarBaseOffset(CGM, OI, cast<ObjCIvarDecl>(Field)) - FirstFieldDelta; // Skip over unnamed or bitfields if (!Field->getIdentifier() || Field->isBitField()) { @@ -3861,12 +3892,16 @@ llvm::Constant *CGObjCCommonMac::BuildIvarLayout( bool hasUnion = false; const llvm::Type *PtrTy = llvm::Type::getInt8PtrTy(VMContext); - if (CGM.getLangOptions().getGCMode() == LangOptions::NonGC) + if (CGM.getLangOptions().getGCMode() == LangOptions::NonGC && + !CGM.getLangOptions().ObjCAutoRefCount) return llvm::Constant::getNullValue(PtrTy); llvm::SmallVector<ObjCIvarDecl*, 32> Ivars; const ObjCInterfaceDecl *OI = OMD->getClassInterface(); - CGM.getContext().DeepCollectObjCIvars(OI, true, Ivars); + if (CGM.getLangOptions().ObjCAutoRefCount) + CGM.getContext().ShallowCollectObjCIvars(OI, Ivars); + else + CGM.getContext().DeepCollectObjCIvars(OI, true, Ivars); llvm::SmallVector<FieldDecl*, 32> RecFields; for (unsigned k = 0, e = Ivars.size(); k != e; ++k) @@ -4743,7 +4778,12 @@ enum MetaDataDlags { CLS_META = 0x1, CLS_ROOT = 0x2, OBJC2_CLS_HIDDEN = 0x10, - CLS_EXCEPTION = 0x20 + CLS_EXCEPTION = 0x20, + + /// (Obsolete) ARC-specific: this class has a .release_ivars method + CLS_HAS_IVAR_RELEASER = 0x40, + /// class was compiled with -fobjc-arr + CLS_COMPILED_BY_ARC = 0x80 // (1<<7) }; /// BuildClassRoTInitializer - generate meta-data for: /// struct _class_ro_t { @@ -4767,6 +4807,10 @@ llvm::GlobalVariable * CGObjCNonFragileABIMac::BuildClassRoTInitializer( const ObjCImplementationDecl *ID) { std::string ClassName = ID->getNameAsString(); std::vector<llvm::Constant*> Values(10); // 11 for 64bit targets! + + if (CGM.getLangOptions().ObjCAutoRefCount) + flags |= CLS_COMPILED_BY_ARC; + Values[ 0] = llvm::ConstantInt::get(ObjCTypes.IntTy, flags); Values[ 1] = llvm::ConstantInt::get(ObjCTypes.IntTy, InstanceStart); Values[ 2] = llvm::ConstantInt::get(ObjCTypes.IntTy, InstanceSize); @@ -4936,7 +4980,7 @@ void CGObjCNonFragileABIMac::GenerateClass(const ObjCImplementationDecl *ID) { ID->getClassInterface()->getVisibility() == HiddenVisibility; if (classIsHidden) flags |= OBJC2_CLS_HIDDEN; - if (ID->getNumIvarInitializers()) + if (ID->hasCXXStructors()) flags |= eClassFlags_ABI2_HasCXXStructors; if (!ID->getClassInterface()->getSuperClass()) { // class is root @@ -4972,7 +5016,7 @@ void CGObjCNonFragileABIMac::GenerateClass(const ObjCImplementationDecl *ID) { flags = CLS; if (classIsHidden) flags |= OBJC2_CLS_HIDDEN; - if (ID->getNumIvarInitializers()) + if (ID->hasCXXStructors()) flags |= eClassFlags_ABI2_HasCXXStructors; if (hasObjCExceptionAttribute(CGM.getContext(), ID->getClassInterface())) @@ -5719,28 +5763,39 @@ CGObjCNonFragileABIMac::GetClassGlobal(const std::string &Name) { return GV; } -llvm::Value *CGObjCNonFragileABIMac::EmitClassRef(CGBuilderTy &Builder, - const ObjCInterfaceDecl *ID) { - llvm::GlobalVariable *&Entry = ClassReferences[ID->getIdentifier()]; - +llvm::Value *CGObjCNonFragileABIMac::EmitClassRefFromId(CGBuilderTy &Builder, + IdentifierInfo *II) { + llvm::GlobalVariable *&Entry = ClassReferences[II]; + if (!Entry) { - std::string ClassName(getClassSymbolPrefix() + ID->getNameAsString()); + std::string ClassName(getClassSymbolPrefix() + II->getName().str()); llvm::GlobalVariable *ClassGV = GetClassGlobal(ClassName); Entry = - new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, - false, llvm::GlobalValue::InternalLinkage, - ClassGV, - "\01L_OBJC_CLASSLIST_REFERENCES_$_"); + new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, + false, llvm::GlobalValue::InternalLinkage, + ClassGV, + "\01L_OBJC_CLASSLIST_REFERENCES_$_"); Entry->setAlignment( - CGM.getTargetData().getABITypeAlignment( - ObjCTypes.ClassnfABIPtrTy)); + CGM.getTargetData().getABITypeAlignment( + ObjCTypes.ClassnfABIPtrTy)); Entry->setSection("__DATA, __objc_classrefs, regular, no_dead_strip"); CGM.AddUsedGlobal(Entry); } - + return Builder.CreateLoad(Entry, "tmp"); } +llvm::Value *CGObjCNonFragileABIMac::EmitClassRef(CGBuilderTy &Builder, + const ObjCInterfaceDecl *ID) { + return EmitClassRefFromId(Builder, ID->getIdentifier()); +} + +llvm::Value *CGObjCNonFragileABIMac::EmitNSAutoreleasePoolClassRef( + CGBuilderTy &Builder) { + IdentifierInfo *II = &CGM.getContext().Idents.get("NSAutoreleasePool"); + return EmitClassRefFromId(Builder, II); +} + llvm::Value * CGObjCNonFragileABIMac::EmitSuperClassRef(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID) { diff --git a/clang/lib/CodeGen/CGObjCRuntime.h b/clang/lib/CodeGen/CGObjCRuntime.h index 866d5d83fe6..7accc70c962 100644 --- a/clang/lib/CodeGen/CGObjCRuntime.h +++ b/clang/lib/CodeGen/CGObjCRuntime.h @@ -205,7 +205,13 @@ public: /// interface decl. virtual llvm::Value *GetClass(CGBuilderTy &Builder, const ObjCInterfaceDecl *OID) = 0; - + + + virtual llvm::Value *EmitNSAutoreleasePoolClassRef(CGBuilderTy &Builder) { + assert(false &&"autoreleasepool unsupported in this ABI"); + return 0; + } + /// EnumerationMutationFunction - Return the function that's called by the /// compiler when a mutation is detected during foreach iteration. virtual llvm::Constant *EnumerationMutationFunction() = 0; diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index a982621be79..187d5a6c3c6 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -151,6 +151,9 @@ void CodeGenFunction::EmitStmt(const Stmt *S) { case Stmt::ObjCForCollectionStmtClass: EmitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(*S)); break; + case Stmt::ObjCAutoreleasePoolStmtClass: + EmitObjCAutoreleasePoolStmt(cast<ObjCAutoreleasePoolStmt>(*S)); + break; case Stmt::CXXTryStmtClass: EmitCXXTryStmt(cast<CXXTryStmt>(*S)); @@ -764,7 +767,7 @@ void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) { } else if (RV->getType()->isAnyComplexType()) { EmitComplexExprIntoAddr(RV, ReturnValue, false); } else { - EmitAggExpr(RV, AggValueSlot::forAddr(ReturnValue, false, true)); + EmitAggExpr(RV, AggValueSlot::forAddr(ReturnValue, Qualifiers(), true)); } EmitBranchThroughCleanup(ReturnBlock); diff --git a/clang/lib/CodeGen/CGValue.h b/clang/lib/CodeGen/CGValue.h index 7f77d552032..1ad6628ec6e 100644 --- a/clang/lib/CodeGen/CGValue.h +++ b/clang/lib/CodeGen/CGValue.h @@ -101,8 +101,6 @@ public: /// bitfields, this is not a simple LLVM pointer, it may be a pointer plus a /// bitrange. class LValue { - // FIXME: alignment? - enum { Simple, // This is a normal l-value, use getAddress(). VectorElt, // This is a vector element l-value (V[i]), use getVector* @@ -318,9 +316,11 @@ public: class AggValueSlot { /// The address. llvm::Value *Addr; + + // Qualifiers + Qualifiers Quals; // Associated flags. - bool VolatileFlag : 1; bool LifetimeFlag : 1; bool RequiresGCollection : 1; @@ -335,25 +335,31 @@ public: static AggValueSlot ignored() { AggValueSlot AV; AV.Addr = 0; - AV.VolatileFlag = AV.LifetimeFlag = AV.RequiresGCollection = AV.IsZeroed =0; + AV.Quals = Qualifiers(); + AV.LifetimeFlag = AV.RequiresGCollection = AV.IsZeroed =0; return AV; } /// forAddr - Make a slot for an aggregate value. /// /// \param Volatile - true if the slot should be volatile-initialized + /// + /// \param Qualifiers - The qualifiers that dictate how the slot + /// should be initialied. Only 'volatile' and the Objective-C + /// lifetime qualifiers matter. + /// /// \param LifetimeExternallyManaged - true if the slot's lifetime /// is being externally managed; false if a destructor should be /// registered for any temporaries evaluated into the slot /// \param RequiresGCollection - true if the slot is located /// somewhere that ObjC GC calls should be emitted for - static AggValueSlot forAddr(llvm::Value *Addr, bool Volatile, + static AggValueSlot forAddr(llvm::Value *Addr, Qualifiers Quals, bool LifetimeExternallyManaged, bool RequiresGCollection = false, bool IsZeroed = false) { AggValueSlot AV; AV.Addr = Addr; - AV.VolatileFlag = Volatile; + AV.Quals = Quals; AV.LifetimeFlag = LifetimeExternallyManaged; AV.RequiresGCollection = RequiresGCollection; AV.IsZeroed = IsZeroed; @@ -362,7 +368,7 @@ public: static AggValueSlot forLValue(LValue LV, bool LifetimeExternallyManaged, bool RequiresGCollection = false) { - return forAddr(LV.getAddress(), LV.isVolatileQualified(), + return forAddr(LV.getAddress(), LV.getQuals(), LifetimeExternallyManaged, RequiresGCollection); } @@ -373,8 +379,14 @@ public: LifetimeFlag = Managed; } + Qualifiers getQualifiers() const { return Quals; } + bool isVolatile() const { - return VolatileFlag; + return Quals.hasVolatile(); + } + + Qualifiers::ObjCLifetime getObjCLifetime() const { + return Quals.getObjCLifetime(); } bool requiresGCollection() const { diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 150cb69b4d1..6ab4b76e75d 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -31,7 +31,7 @@ using namespace CodeGen; CodeGenFunction::CodeGenFunction(CodeGenModule &cgm) : CodeGenTypeCache(cgm), CGM(cgm), Target(CGM.getContext().Target), Builder(cgm.getModule().getContext()), - BlockInfo(0), BlockPointer(0), + AutoreleaseResult(false), BlockInfo(0), BlockPointer(0), NormalCleanupDest(0), EHCleanupDest(0), NextCleanupDestIndex(1), ExceptionSlot(0), EHSelectorSlot(0), DebugInfo(0), DisableDebugInfo(false), DidCallStackSave(false), @@ -142,6 +142,13 @@ void CodeGenFunction::FinishFunction(SourceLocation EndLoc) { assert(BreakContinueStack.empty() && "mismatched push/pop in break/continue stack!"); + // Pop any cleanups that might have been associated with the + // parameters. Do this in whatever block we're currently in; it's + // important to do this before we enter the return block or return + // edges will be *really* confused. + if (EHStack.stable_begin() != PrologueCleanupDepth) + PopCleanupBlocks(PrologueCleanupDepth); + // Emit function epilog (to return). EmitReturnBlock(); @@ -311,9 +318,19 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, ReturnValue = CurFn->arg_begin(); } else { ReturnValue = CreateIRTemp(RetTy, "retval"); + + // Tell the epilog emitter to autorelease the result. We do this + // now so that various specialized functions can suppress it + // during their IR-generation. + if (getLangOptions().ObjCAutoRefCount && + !CurFnInfo->isReturnsRetained() && + RetTy->isObjCRetainableType()) + AutoreleaseResult = true; } EmitStartEHSpec(CurCodeDecl); + + PrologueCleanupDepth = EHStack.stable_begin(); EmitFunctionProlog(*CurFnInfo, CurFn, Args); if (D && isa<CXXMethodDecl>(D) && cast<CXXMethodDecl>(D)->isInstance()) diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index bb8fd8e2430..f35022810f7 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -18,6 +18,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/CharUnits.h" +#include "clang/Frontend/CodeGenOptions.h" #include "clang/Basic/ABI.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/DenseMap.h" @@ -63,6 +64,7 @@ namespace clang { class ObjCAtTryStmt; class ObjCAtThrowStmt; class ObjCAtSynchronizedStmt; + class ObjCAutoreleasePoolStmt; namespace CodeGen { class CodeGenTypes; @@ -568,6 +570,10 @@ public: /// CurGD - The GlobalDecl for the current function being compiled. GlobalDecl CurGD; + /// PrologueCleanupDepth - The cleanup depth enclosing all the + /// cleanups associated with the parameters. + EHScopeStack::stable_iterator PrologueCleanupDepth; + /// ReturnBlock - Unified return block. JumpDest ReturnBlock; @@ -584,6 +590,9 @@ public: bool CatchUndefined; + /// In ARC, whether we should autorelease the return value. + bool AutoreleaseResult; + const CodeGen::CGBlockInfo *BlockInfo; llvm::Value *BlockPointer; @@ -1048,6 +1057,9 @@ public: void disableDebugInfo() { DisableDebugInfo = true; } void enableDebugInfo() { DisableDebugInfo = false; } + bool shouldUseFusedARCCalls() { + return CGM.getCodeGenOpts().OptimizationLevel == 0; + } const LangOptions &getLangOptions() const { return CGM.getLangOptions(); } @@ -1345,7 +1357,8 @@ public: /// CreateAggTemp - Create a temporary memory object for the given /// aggregate type. AggValueSlot CreateAggTemp(QualType T, const llvm::Twine &Name = "tmp") { - return AggValueSlot::forAddr(CreateMemTemp(T, Name), false, false); + return AggValueSlot::forAddr(CreateMemTemp(T, Name), T.getQualifiers(), + false); } /// Emit a cast to void* in the appropriate address space. @@ -1379,12 +1392,11 @@ public: /// EmitAnyExprToMem - Emits the code necessary to evaluate an /// arbitrary expression into the given memory location. void EmitAnyExprToMem(const Expr *E, llvm::Value *Location, - bool IsLocationVolatile, - bool IsInitializer); + Qualifiers Quals, bool IsInitializer); /// EmitExprAsInit - Emits the code necessary to initialize a /// location in memory with the given initializer. - void EmitExprAsInit(const Expr *init, const VarDecl *var, + void EmitExprAsInit(const Expr *init, const ValueDecl *D, llvm::Value *loc, CharUnits alignment, bool capturedByInit); @@ -1584,6 +1596,10 @@ public: /// This function can be called with a null (unreachable) insert point. void EmitVarDecl(const VarDecl &D); + void EmitScalarInit(const Expr *init, const ValueDecl *D, + llvm::Value *addr, bool capturedByInit, + bool isVolatile, unsigned alignment, QualType type); + typedef void SpecialInitFn(CodeGenFunction &Init, const VarDecl &D, llvm::Value *Address); @@ -1709,6 +1725,7 @@ public: void EmitObjCAtTryStmt(const ObjCAtTryStmt &S); void EmitObjCAtThrowStmt(const ObjCAtThrowStmt &S); void EmitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt &S); + void EmitObjCAutoreleasePoolStmt(const ObjCAutoreleasePoolStmt &S); llvm::Constant *getUnwindResumeFn(); llvm::Constant *getUnwindResumeOrRethrowFn(); @@ -1961,6 +1978,64 @@ public: RValue EmitObjCMessageExpr(const ObjCMessageExpr *E, ReturnValueSlot Return = ReturnValueSlot()); + /// Retrieves the default cleanup kind for an ARC cleanup. + /// Except under -fobjc-arc-eh, ARC cleanups are normal-only. + CleanupKind getARCCleanupKind() { + return CGM.getCodeGenOpts().ObjCAutoRefCountExceptions + ? NormalAndEHCleanup : NormalCleanup; + } + + // ARC primitives. + void EmitARCInitWeak(llvm::Value *value, llvm::Value *addr); + void EmitARCDestroyWeak(llvm::Value *addr); + llvm::Value *EmitARCLoadWeak(llvm::Value *addr); + llvm::Value *EmitARCLoadWeakRetained(llvm::Value *addr); + llvm::Value *EmitARCStoreWeak(llvm::Value *value, llvm::Value *addr, + bool ignored); + void EmitARCCopyWeak(llvm::Value *dst, llvm::Value *src); + void EmitARCMoveWeak(llvm::Value *dst, llvm::Value *src); + llvm::Value *EmitARCRetainAutorelease(QualType type, llvm::Value *value); + llvm::Value *EmitARCRetainAutoreleaseNonBlock(llvm::Value *value); + llvm::Value *EmitARCStoreStrong(LValue addr, QualType type, + llvm::Value *value, bool ignored); + llvm::Value *EmitARCStoreStrongCall(llvm::Value *addr, llvm::Value *value, + bool ignored); + llvm::Value *EmitARCRetain(QualType type, llvm::Value *value); + llvm::Value *EmitARCRetainNonBlock(llvm::Value *value); + llvm::Value *EmitARCRetainBlock(llvm::Value *value); + void EmitARCRelease(llvm::Value *value, bool precise); + llvm::Value *EmitARCAutorelease(llvm::Value *value); + llvm::Value *EmitARCAutoreleaseReturnValue(llvm::Value *value); + llvm::Value *EmitARCRetainAutoreleaseReturnValue(llvm::Value *value); + llvm::Value *EmitARCRetainAutoreleasedReturnValue(llvm::Value *value); + + std::pair<LValue,llvm::Value*> + EmitARCStoreAutoreleasing(const BinaryOperator *e); + std::pair<LValue,llvm::Value*> + EmitARCStoreStrong(const BinaryOperator *e, bool ignored); + + llvm::Value *EmitObjCProduceObject(QualType T, llvm::Value *Ptr); + llvm::Value *EmitObjCConsumeObject(QualType T, llvm::Value *Ptr); + llvm::Value *EmitObjCExtendObjectLifetime(QualType T, llvm::Value *Ptr); + + llvm::Value *EmitARCRetainScalarExpr(const Expr *expr); + llvm::Value *EmitARCRetainAutoreleaseScalarExpr(const Expr *expr); + + void PushARCReleaseCleanup(CleanupKind kind, QualType type, + llvm::Value *addr, bool precise); + void PushARCWeakReleaseCleanup(CleanupKind kind, QualType type, + llvm::Value *addr); + void PushARCFieldReleaseCleanup(CleanupKind cleanupKind, + const FieldDecl *Field); + void PushARCFieldWeakReleaseCleanup(CleanupKind cleanupKind, + const FieldDecl *Field); + + void EmitObjCAutoreleasePoolPop(llvm::Value *Ptr); + llvm::Value *EmitObjCAutoreleasePoolPush(); + llvm::Value *EmitObjCMRRAutoreleasePoolPush(); + void EmitObjCAutoreleasePoolCleanup(llvm::Value *Ptr); + void EmitObjCMRRAutoreleasePoolPop(llvm::Value *Ptr); + /// EmitReferenceBindingToExpr - Emits a reference binding to the passed in /// expression. Will emit a temporary variable if E is not an LValue. RValue EmitReferenceBindingToExpr(const Expr* E, diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 7a1a968259f..78c57b4e3f2 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -64,7 +64,7 @@ CodeGenModule::CodeGenModule(ASTContext &C, const CodeGenOptions &CGO, ABI(createCXXABI(*this)), Types(C, M, TD, getTargetCodeGenInfo().getABIInfo(), ABI), TBAA(0), - VTables(*this), Runtime(0), DebugInfo(0), + VTables(*this), Runtime(0), DebugInfo(0), ARCData(0), RRData(0), CFConstantStringClassRef(0), ConstantStringClassRef(0), VMContext(M.getContext()), NSConcreteGlobalBlockDecl(0), NSConcreteStackBlockDecl(0), @@ -88,6 +88,10 @@ CodeGenModule::CodeGenModule(ASTContext &C, const CodeGenOptions &CGO, Block.GlobalUniqueCount = 0; + if (C.getLangOptions().ObjCAutoRefCount) + ARCData = new ARCEntrypoints(); + RRData = new RREntrypoints(); + // Initialize the type cache. llvm::LLVMContext &LLVMContext = M.getContext(); VoidTy = llvm::Type::getVoidTy(LLVMContext); @@ -108,6 +112,8 @@ CodeGenModule::~CodeGenModule() { delete &ABI; delete TBAA; delete DebugInfo; + delete ARCData; + delete RRData; } void CodeGenModule::createObjCRuntime() { @@ -830,7 +836,8 @@ void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD) { llvm::Constant * CodeGenModule::GetOrCreateLLVMFunction(llvm::StringRef MangledName, const llvm::Type *Ty, - GlobalDecl D, bool ForVTable) { + GlobalDecl D, bool ForVTable, + llvm::Attributes ExtraAttrs) { // Lookup the entry, lazily creating it if necessary. llvm::GlobalValue *Entry = GetGlobalValue(MangledName); if (Entry) { @@ -869,6 +876,8 @@ CodeGenModule::GetOrCreateLLVMFunction(llvm::StringRef MangledName, assert(F->getName() == MangledName && "name was uniqued!"); if (D.getDecl()) SetFunctionAttributes(D, F, IsIncompleteFunction); + if (ExtraAttrs != llvm::Attribute::None) + F->addFnAttr(ExtraAttrs); // This is the first use or definition of a mangled name. If there is a // deferred decl with this name, remember that we need to emit it at the end @@ -937,8 +946,10 @@ llvm::Constant *CodeGenModule::GetAddrOfFunction(GlobalDecl GD, /// type and name. llvm::Constant * CodeGenModule::CreateRuntimeFunction(const llvm::FunctionType *FTy, - llvm::StringRef Name) { - return GetOrCreateLLVMFunction(Name, FTy, GlobalDecl(), /*ForVTable=*/false); + llvm::StringRef Name, + llvm::Attributes ExtraAttrs) { + return GetOrCreateLLVMFunction(Name, FTy, GlobalDecl(), /*ForVTable=*/false, + ExtraAttrs); } static bool DeclIsConstantGlobal(ASTContext &Context, const VarDecl *D, @@ -1997,6 +2008,7 @@ void CodeGenModule::EmitObjCIvarInitializations(ObjCImplementationDecl *D) { false, true, false, ObjCMethodDecl::Required); D->addInstanceMethod(DTORMethod); CodeGenFunction(*this).GenerateObjCCtorDtorMethod(D, DTORMethod, false); + D->setHasCXXStructors(true); } // If the implementation doesn't have any ivar initializers, we don't need @@ -2015,6 +2027,7 @@ void CodeGenModule::EmitObjCIvarInitializations(ObjCImplementationDecl *D) { ObjCMethodDecl::Required); D->addInstanceMethod(CTORMethod); CodeGenFunction(*this).GenerateObjCCtorDtorMethod(D, CTORMethod, true); + D->setHasCXXStructors(true); } /// EmitNamespace - Emit all declarations in a namespace. diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 09741dfada4..7321ac45f44 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -131,6 +131,71 @@ namespace CodeGen { /// The alignment of a pointer into the generic address space. unsigned char PointerAlignInBytes; }; + +struct RREntrypoints { + RREntrypoints() { memset(this, 0, sizeof(*this)); } + /// void objc_autoreleasePoolPop(void*); + llvm::Constant *objc_autoreleasePoolPop; + + /// void *objc_autoreleasePoolPush(void); + llvm::Constant *objc_autoreleasePoolPush; +}; + +struct ARCEntrypoints { + ARCEntrypoints() { memset(this, 0, sizeof(*this)); } + + /// id objc_autorelease(id); + llvm::Constant *objc_autorelease; + + /// id objc_autoreleaseReturnValue(id); + llvm::Constant *objc_autoreleaseReturnValue; + + /// void objc_copyWeak(id *dest, id *src); + llvm::Constant *objc_copyWeak; + + /// void objc_destroyWeak(id*); + llvm::Constant *objc_destroyWeak; + + /// id objc_initWeak(id*, id); + llvm::Constant *objc_initWeak; + + /// id objc_loadWeak(id*); + llvm::Constant *objc_loadWeak; + + /// id objc_loadWeakRetained(id*); + llvm::Constant *objc_loadWeakRetained; + + /// void objc_moveWeak(id *dest, id *src); + llvm::Constant *objc_moveWeak; + + /// id objc_retain(id); + llvm::Constant *objc_retain; + + /// id objc_retainAutorelease(id); + llvm::Constant *objc_retainAutorelease; + + /// id objc_retainAutoreleaseReturnValue(id); + llvm::Constant *objc_retainAutoreleaseReturnValue; + + /// id objc_retainAutoreleasedReturnValue(id); + llvm::Constant *objc_retainAutoreleasedReturnValue; + + /// id objc_retainBlock(id); + llvm::Constant *objc_retainBlock; + + /// void objc_release(id); + llvm::Constant *objc_release; + + /// id objc_storeStrong(id*, id); + llvm::Constant *objc_storeStrong; + + /// id objc_storeWeak(id*, id); + llvm::Constant *objc_storeWeak; + + /// A void(void) inline asm to use to mark that the return value of + /// a call will be immediately retain. + llvm::InlineAsm *retainAutoreleasedReturnValueMarker; +}; /// CodeGenModule - This class organizes the cross-function state that is used /// while generating LLVM code. @@ -157,6 +222,8 @@ class CodeGenModule : public CodeGenTypeCache { CGObjCRuntime* Runtime; CGDebugInfo* DebugInfo; + ARCEntrypoints *ARCData; + RREntrypoints *RRData; // WeakRefReferences - A set of references that have only been seen via // a weakref so far. This is used to remove the weak of the reference if we ever @@ -275,6 +342,16 @@ public: /// getCXXABI() - Return a reference to the configured C++ ABI. CGCXXABI &getCXXABI() { return ABI; } + ARCEntrypoints &getARCEntrypoints() const { + assert(getLangOptions().ObjCAutoRefCount && ARCData != 0); + return *ARCData; + } + + RREntrypoints &getRREntrypoints() const { + assert(RRData != 0); + return *RRData; + } + llvm::Value *getStaticLocalDeclAddress(const VarDecl *VD) { return StaticLocalDeclMap[VD]; } @@ -474,7 +551,7 @@ public: /// created). llvm::Constant *GetAddrOfConstantCString(const std::string &str, const char *GlobalName=0); - + /// GetAddrOfCXXConstructor - Return the address of the constructor of the /// given type. llvm::GlobalValue *GetAddrOfCXXConstructor(const CXXConstructorDecl *ctor, @@ -514,7 +591,9 @@ public: /// CreateRuntimeFunction - Create a new runtime function with the specified /// type and name. llvm::Constant *CreateRuntimeFunction(const llvm::FunctionType *Ty, - llvm::StringRef Name); + llvm::StringRef Name, + llvm::Attributes ExtraAttrs = + llvm::Attribute::None); /// CreateRuntimeVariable - Create a new runtime global variable with the /// specified type and name. llvm::Constant *CreateRuntimeVariable(const llvm::Type *Ty, @@ -642,7 +721,9 @@ private: llvm::Constant *GetOrCreateLLVMFunction(llvm::StringRef MangledName, const llvm::Type *Ty, GlobalDecl D, - bool ForVTable); + bool ForVTable, + llvm::Attributes ExtraAttrs = + llvm::Attribute::None); llvm::Constant *GetOrCreateLLVMGlobal(llvm::StringRef MangledName, const llvm::PointerType *PTy, const VarDecl *D, diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 12ef9bd030b..7e71df1268a 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -802,10 +802,27 @@ bool ItaniumCXXABI::NeedsArrayCookie(const CXXNewExpr *expr) { if (expr->doesUsualArrayDeleteWantSize()) return true; + // Automatic Reference Counting: + // We need an array cookie for pointers with strong or weak lifetime. + QualType AllocatedType = expr->getAllocatedType(); + if (getContext().getLangOptions().ObjCAutoRefCount && + AllocatedType->isObjCLifetimeType()) { + switch (AllocatedType.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + return false; + + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Weak: + return true; + } + } + // Otherwise, if the class has a non-trivial destructor, it always // needs a cookie. const CXXRecordDecl *record = - expr->getAllocatedType()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); + AllocatedType->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); return (record && !record->hasTrivialDestructor()); } @@ -816,6 +833,22 @@ bool ItaniumCXXABI::NeedsArrayCookie(const CXXDeleteExpr *expr, if (expr->doesUsualArrayDeleteWantSize()) return true; + // Automatic Reference Counting: + // We need an array cookie for pointers with strong or weak lifetime. + if (getContext().getLangOptions().ObjCAutoRefCount && + elementType->isObjCLifetimeType()) { + switch (elementType.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + return false; + + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Weak: + return true; + } + } + // Otherwise, if the class has a non-trivial destructor, it always // needs a cookie. const CXXRecordDecl *record = diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp index 043ead7e62d..9a6cc208e5c 100644 --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -2277,6 +2277,10 @@ public: return 13; } + llvm::StringRef getARCRetainAutoreleasedReturnValueMarker() const { + return "mov\tr7, r7\t\t@ marker for objc_retainAutoreleaseReturnValue"; + } + bool initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF, llvm::Value *Address) const { CodeGen::CGBuilderTy &Builder = CGF.Builder; @@ -2290,8 +2294,6 @@ public: return false; } - - }; } diff --git a/clang/lib/CodeGen/TargetInfo.h b/clang/lib/CodeGen/TargetInfo.h index 4f59eb619e9..dbb09b1614d 100644 --- a/clang/lib/CodeGen/TargetInfo.h +++ b/clang/lib/CodeGen/TargetInfo.h @@ -111,6 +111,20 @@ namespace clang { const llvm::Type* Ty) const { return Ty; } + + /// Retrieve the address of a function to call immediately before + /// calling objc_retainAutoreleasedReturnValue. The + /// implementation of objc_autoreleaseReturnValue sniffs the + /// instruction stream following its return address to decide + /// whether it's a call to objc_retainAutoreleasedReturnValue. + /// This can be prohibitively expensive, depending on the + /// relocation model, and so on some targets it instead sniffs for + /// a particular instruction sequence. This functions returns + /// that instruction sequence in inline assembly, which will be + /// empty if none is required. + virtual llvm::StringRef getARCRetainAutoreleasedReturnValueMarker() const { + return ""; + } }; } diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index d9199154104..8079e1983f3 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -201,7 +201,8 @@ ToolChain::CXXStdlibType ToolChain::GetCXXStdlibType(const ArgList &Args) const{ } void ToolChain::AddClangCXXStdlibIncludeArgs(const ArgList &Args, - ArgStringList &CmdArgs) const { + ArgStringList &CmdArgs, + bool ObjCXXAutoRefCount) const { CXXStdlibType Type = GetCXXStdlibType(Args); switch (Type) { @@ -209,10 +210,16 @@ void ToolChain::AddClangCXXStdlibIncludeArgs(const ArgList &Args, CmdArgs.push_back("-nostdinc++"); CmdArgs.push_back("-cxx-isystem"); CmdArgs.push_back("/usr/include/c++/v1"); + + if (ObjCXXAutoRefCount) + CmdArgs.push_back("-fobjc-arc-cxxlib=libc++"); break; case ToolChain::CST_Libstdcxx: // Currently handled by the mass of goop in InitHeaderSearch. + + if (ObjCXXAutoRefCount) + CmdArgs.push_back("-fobjc-arc-cxxlib=libstdc++"); break; } } diff --git a/clang/lib/Driver/ToolChains.cpp b/clang/lib/Driver/ToolChains.cpp index ca613e3d6c8..35e7d80c295 100644 --- a/clang/lib/Driver/ToolChains.cpp +++ b/clang/lib/Driver/ToolChains.cpp @@ -26,6 +26,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" @@ -41,7 +42,8 @@ using namespace clang::driver::toolchains; /// Darwin - Darwin tool chain for i386 and x86_64. Darwin::Darwin(const HostInfo &Host, const llvm::Triple& Triple) - : ToolChain(Host, Triple), TargetInitialized(false) + : ToolChain(Host, Triple), TargetInitialized(false), + ARCRuntimeForSimulator(ARCSimulator_None) { // Compute the initial Darwin version based on the host. bool HadExtra; @@ -70,6 +72,25 @@ bool Darwin::HasNativeLLVMSupport() const { return true; } +/// Darwin provides an ARC runtime starting in MacOS X 10.7 and iOS 5.0. +bool Darwin::HasARCRuntime() const { + // FIXME: Remove this once there is a proper way to detect an ARC runtime + // for the simulator. + switch (ARCRuntimeForSimulator) { + case ARCSimulator_None: + break; + case ARCSimulator_HasARCRuntime: + return true; + case ARCSimulator_NoARCRuntime: + return false; + } + + if (isTargetIPhoneOS()) + return !isIPhoneOSVersionLT(5); + else + return !isMacosxVersionLT(10, 7); +} + // FIXME: Can we tablegen this? static const char *GetArmArchForMArch(llvm::StringRef Value) { if (Value == "armv6k") @@ -320,6 +341,30 @@ void DarwinClang::AddLinkSearchPathArgs(const ArgList &Args, CmdArgs.push_back(Args.MakeArgString("-L" + P.str())); } +void DarwinClang::AddLinkARCArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + + CmdArgs.push_back("-force_load"); + llvm::sys::Path P(getDriver().ClangExecutable); + P.eraseComponent(); // 'clang' + P.eraseComponent(); // 'bin' + P.appendComponent("lib"); + P.appendComponent("arc"); + P.appendComponent("libarclite_"); + std::string s = P.str(); + // Mash in the platform. + if (isTargetIPhoneOS()) + s += "iphoneos"; + // FIXME: isTargetIphoneOSSimulator() is not returning true. + else if (ARCRuntimeForSimulator != ARCSimulator_None) + s += "iphonesimulator"; + else + s += "macosx"; + s += ".a"; + + CmdArgs.push_back(Args.MakeArgString(s)); +} + void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args, ArgStringList &CmdArgs) const { // Darwin doesn't support real static executables, don't link any runtime @@ -389,6 +434,35 @@ void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args, } } +static inline llvm::StringRef SimulatorVersionDefineName() { + return "__IPHONE_OS_VERSION_MIN_REQUIRED"; +} + +/// \brief Parse the simulator version define: +/// __IPHONE_OS_VERSION_MIN_REQUIRED=([0-9])([0-9][0-9])([0-9][0-9]) +// and return the grouped values as integers, e.g: +// __IPHONE_OS_VERSION_MIN_REQUIRED=40201 +// will return Major=4, Minor=2, Micro=1. +static bool GetVersionFromSimulatorDefine(llvm::StringRef define, + unsigned &Major, unsigned &Minor, + unsigned &Micro) { + assert(define.startswith(SimulatorVersionDefineName())); + llvm::StringRef name, version; + llvm::tie(name, version) = define.split('='); + if (version.empty()) + return false; + std::string verstr = version.str(); + char *end; + unsigned num = (unsigned) strtol(verstr.c_str(), &end, 10); + if (*end != '\0') + return false; + Major = num / 10000; + num = num % 10000; + Minor = num / 100; + Micro = num % 100; + return true; +} + void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { const OptTable &Opts = getDriver().getOpts(); @@ -396,6 +470,27 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { Arg *iOSVersion = Args.getLastArg(options::OPT_miphoneos_version_min_EQ); Arg *iOSSimVersion = Args.getLastArg( options::OPT_mios_simulator_version_min_EQ); + + // FIXME: HACK! When compiling for the simulator we don't get a + // '-miphoneos-version-min' to help us know whether there is an ARC runtime + // or not; try to parse a __IPHONE_OS_VERSION_MIN_REQUIRED + // define passed in command-line. + if (!iOSVersion) { + for (arg_iterator it = Args.filtered_begin(options::OPT_D), + ie = Args.filtered_end(); it != ie; ++it) { + llvm::StringRef define = (*it)->getValue(Args); + if (define.startswith(SimulatorVersionDefineName())) { + unsigned Major, Minor, Micro; + if (GetVersionFromSimulatorDefine(define, Major, Minor, Micro) && + Major < 10 && Minor < 100 && Micro < 100) { + ARCRuntimeForSimulator = Major < 5 ? ARCSimulator_NoARCRuntime + : ARCSimulator_HasARCRuntime; + } + break; + } + } + } + if (OSXVersion && (iOSVersion || iOSSimVersion)) { getDriver().Diag(clang::diag::err_drv_argument_not_allowed_with) << OSXVersion->getAsString(Args) diff --git a/clang/lib/Driver/ToolChains.h b/clang/lib/Driver/ToolChains.h index ace9b847919..73258744739 100644 --- a/clang/lib/Driver/ToolChains.h +++ b/clang/lib/Driver/ToolChains.h @@ -57,6 +57,16 @@ private: // the argument translation business. mutable bool TargetInitialized; + // FIXME: Remove this once there is a proper way to detect an ARC runtime + // for the simulator. + public: + mutable enum { + ARCSimulator_None, + ARCSimulator_HasARCRuntime, + ARCSimulator_NoARCRuntime + } ARCRuntimeForSimulator; + +private: /// Whether we are targeting iPhoneOS target. mutable bool TargetIsIPhoneOS; @@ -157,6 +167,10 @@ public: virtual void AddLinkSearchPathArgs(const ArgList &Args, ArgStringList &CmdArgs) const = 0; + /// AddLinkARCArgs - Add the linker arguments to link the ARC runtime library. + virtual void AddLinkARCArgs(const ArgList &Args, + ArgStringList &CmdArgs) const = 0; + /// AddLinkRuntimeLibArgs - Add the linker arguments to link the compiler /// runtime library. virtual void AddLinkRuntimeLibArgs(const ArgList &Args, @@ -170,6 +184,8 @@ public: virtual bool HasNativeLLVMSupport() const; + virtual bool HasARCRuntime() const; + virtual DerivedArgList *TranslateArgs(const DerivedArgList &Args, const char *BoundArch) const; @@ -257,6 +273,8 @@ public: virtual void AddCCKextLibArgs(const ArgList &Args, ArgStringList &CmdArgs) const; + virtual void AddLinkARCArgs(const ArgList &Args, + ArgStringList &CmdArgs) const; /// } }; diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index 0e85a9fd4f2..1947827438f 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -30,6 +30,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Support/Host.h" #include "llvm/Support/Process.h" +#include "llvm/Support/ErrorHandling.h" #include "InputInfo.h" #include "ToolChains.h" @@ -147,6 +148,12 @@ static void AddLinkerInputs(const ToolChain &TC, } } +/// \brief Determine whether Objective-C automated reference counting is +/// enabled. +static bool isObjCAutoRefCount(const ArgList &Args) { + return Args.hasFlag(options::OPT_fobjc_arc, options::OPT_fno_objc_arc, false); +} + static void addProfileRT(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs) { if (Args.hasArg(options::OPT_fprofile_arcs) || @@ -318,8 +325,12 @@ void Clang::AddPreprocessingOptions(const Driver &D, // Add C++ include arguments, if needed. types::ID InputType = Inputs[0].getType(); - if (types::isCXX(InputType)) - getToolChain().AddClangCXXStdlibIncludeArgs(Args, CmdArgs); + if (types::isCXX(InputType)) { + bool ObjCXXAutoRefCount + = types::isObjC(InputType) && isObjCAutoRefCount(Args); + getToolChain().AddClangCXXStdlibIncludeArgs(Args, CmdArgs, + ObjCXXAutoRefCount); + } // Add -Wp, and -Xassembler if using the preprocessor. @@ -1542,6 +1553,24 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, options::OPT_fno_lax_vector_conversions)) CmdArgs.push_back("-fno-lax-vector-conversions"); + // Allow -fno-objc-arr to trump -fobjc-arr/-fobjc-arc. + // NOTE: This logic is duplicated in ToolChains.cpp. + bool ARC = isObjCAutoRefCount(Args); + if (ARC) { + CmdArgs.push_back("-fobjc-arc"); + + // Certain deployment targets don't have runtime support. + if (!getToolChain().HasARCRuntime()) + CmdArgs.push_back("-fobjc-no-arc-runtime"); + + // Allow the user to enable full exceptions code emission. + // We define off for Objective-CC, on for Objective-C++. + if (Args.hasFlag(options::OPT_fobjc_arc_exceptions, + options::OPT_fno_objc_arc_exceptions, + /*default*/ types::isCXX(InputType))) + CmdArgs.push_back("-fobjc-arc-exceptions"); + } + // -fobjc-infer-related-result-type is the default, except in the Objective-C // rewriter. if (IsRewriter) @@ -1553,7 +1582,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (!GCArg) GCArg = Args.getLastArg(options::OPT_fobjc_gc); if (GCArg) { - if (getToolChain().SupportsObjCGC()) { + if (ARC) { + D.Diag(clang::diag::err_drv_objc_gc_arr) + << GCArg->getAsString(Args); + } else if (getToolChain().SupportsObjCGC()) { GCArg->render(Args, CmdArgs); } else { // FIXME: We should move this to a hard error. @@ -3141,6 +3173,12 @@ void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA, getDarwinToolChain().AddLinkSearchPathArgs(Args, CmdArgs); + // In ARC, if we don't have runtime support, link in the runtime + // stubs. We have to do this *before* adding any of the normal + // linker inputs so that its initializer gets run first. + if (!getDarwinToolChain().HasARCRuntime() && isObjCAutoRefCount(Args)) + getDarwinToolChain().AddLinkARCArgs(Args, CmdArgs); + AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); if (LinkingOutput) { diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index c929b4926d8..1324926f94e 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -169,6 +169,8 @@ static void CodeGenOptsToArgs(const CodeGenOptions &Opts, Res.push_back("-fno-use-cxa-atexit"); if (Opts.CXXCtorDtorAliases) Res.push_back("-mconstructor-aliases"); + if (Opts.ObjCAutoRefCountExceptions) + Res.push_back("-fobjc-arc-eh"); if (!Opts.DebugPass.empty()) { Res.push_back("-mdebug-pass"); Res.push_back(Opts.DebugPass); @@ -670,6 +672,10 @@ static void LangOptsToArgs(const LangOptions &Opts, Res.push_back("-fobjc-gc-only"); } } + if (Opts.ObjCAutoRefCount) + Res.push_back("-fobjc-arc"); + if (Opts.ObjCNoAutoRefCountRuntime) + Res.push_back("-fobjc-no-arc-runtime"); if (!Opts.ObjCInferRelatedResultType) Res.push_back("-fno-objc-infer-related-result-type"); @@ -951,6 +957,7 @@ static void ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, (Opts.OptimizationLevel > 1 && !Opts.OptimizeSize); Opts.AsmVerbose = Args.hasArg(OPT_masm_verbose); + Opts.ObjCAutoRefCountExceptions = Args.hasArg(OPT_fobjc_arc_exceptions); Opts.CXAAtExit = !Args.hasArg(OPT_fno_use_cxa_atexit); Opts.CXXCtorDtorAliases = Args.hasArg(OPT_mconstructor_aliases); Opts.CodeModel = Args.getLastArgValue(OPT_mcode_model); @@ -1480,17 +1487,26 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, if (Args.hasArg(OPT_fno_operator_names)) Opts.CXXOperatorNames = 0; + if (Opts.ObjC1) { + if (Args.hasArg(OPT_fobjc_gc_only)) + Opts.setGCMode(LangOptions::GCOnly); + else if (Args.hasArg(OPT_fobjc_gc)) + Opts.setGCMode(LangOptions::HybridGC); + else if (Args.hasArg(OPT_fobjc_arc)) { + Opts.ObjCAutoRefCount = 1; + if (!Args.hasArg(OPT_fobjc_nonfragile_abi)) + Diags.Report(diag::err_arc_nonfragile_abi); + if (Args.hasArg(OPT_fobjc_no_arc_runtime)) + Opts.ObjCNoAutoRefCountRuntime = 1; + } + + if (Args.hasArg(OPT_fno_objc_infer_related_result_type)) + Opts.ObjCInferRelatedResultType = 0; + } + if (Args.hasArg(OPT_fgnu89_inline)) Opts.GNUInline = 1; - if (Args.hasArg(OPT_fobjc_gc_only)) - Opts.setGCMode(LangOptions::GCOnly); - else if (Args.hasArg(OPT_fobjc_gc)) - Opts.setGCMode(LangOptions::HybridGC); - - if (Args.hasArg(OPT_fno_objc_infer_related_result_type)) - Opts.ObjCInferRelatedResultType = 0; - if (Args.hasArg(OPT_fapple_kext)) { if (!Opts.CPlusPlus) Diags.Report(diag::warn_c_kext); @@ -1715,6 +1731,19 @@ static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args, Opts.addRemappedFile(Split.first, Split.second); } + + if (Arg *A = Args.getLastArg(OPT_fobjc_arc_cxxlib_EQ)) { + llvm::StringRef Name = A->getValue(Args); + unsigned Library = llvm::StringSwitch<unsigned>(Name) + .Case("libc++", ARCXX_libcxx) + .Case("libstdc++", ARCXX_libstdcxx) + .Case("none", ARCXX_nolib) + .Default(~0U); + if (Library == ~0U) + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; + else + Opts.ObjCXXARCStandardLibrary = (ObjCXXARCStandardLibraryKind)Library; + } } static void ParsePreprocessorOutputArgs(PreprocessorOutputOptions &Opts, diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index 147a8df0376..f1e1d47713b 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -221,6 +221,125 @@ static void DefineExactWidthIntType(TargetInfo::IntType Ty, ConstSuffix); } +/// \brief Add definitions required for a smooth interaction between +/// Objective-C++ automatic reference counting and libc++. +static void AddObjCXXARCLibcxxDefines(const LangOptions &LangOpts, + MacroBuilder &Builder) { + Builder.defineMacro("_LIBCPP_PREDEFINED_OBJC_ARC_ADDRESSOF"); + + std::string Result; + { + // Provide overloads of the function std::__1::addressof() that accept + // references to lifetime-qualified objects. libc++'s (more general) + // std::__1::addressof() template fails to instantiate with such types, + // because it attempts to convert the object to a char& before + // dereferencing. + llvm::raw_string_ostream Out(Result); + + Out << "#pragma clang diagnostic push\n" + << "#pragma clang diagnostic ignored \"-Wc++0x-extensions\"\n" + << "namespace std { inline namespace __1 {\n" + << "\n"; + + Out << "template <class _Tp>\n" + << "inline __attribute__ ((__visibility__(\"hidden\"), " + << "__always_inline__))\n" + << "__attribute__((objc_lifetime(strong))) _Tp*\n" + << "addressof(__attribute__((objc_lifetime(strong))) _Tp& __x) {\n" + << " return &__x;\n" + << "}\n" + << "\n"; + + if (!LangOpts.ObjCNoAutoRefCountRuntime) { + Out << "template <class _Tp>\n" + << "inline __attribute__ ((__visibility__(\"hidden\")," + << "__always_inline__))\n" + << "__attribute__((objc_lifetime(weak))) _Tp*\n" + << "addressof(__attribute__((objc_lifetime(weak))) _Tp& __x) {\n" + << " return &__x;\n" + << "};\n" + << "\n"; + } + + Out << "template <class _Tp>\n" + << "inline __attribute__ ((__visibility__(\"hidden\")," + << "__always_inline__))\n" + << "__attribute__((objc_lifetime(autoreleasing))) _Tp*\n" + << "addressof(__attribute__((objc_lifetime(autoreleasing))) _Tp& __x) " + << "{\n" + << " return &__x;\n" + << "}\n" + << "\n"; + + Out << "template <class _Tp>\n" + << "inline __attribute__ ((__visibility__(\"hidden\"), " + << "__always_inline__))\n" + << "__unsafe_unretained _Tp* addressof(__unsafe_unretained _Tp& __x)" + << " {\n" + << " return &__x;\n" + << "}\n"; + + Out << "\n" + << "} }\n" + << "#pragma clang diagnostic pop\n" + << "\n"; + } + Builder.append(Result); +} + +/// \brief Add definitions required for a smooth interaction between +/// Objective-C++ automated reference counting and libstdc++ (4.2). +static void AddObjCXXARCLibstdcxxDefines(const LangOptions &LangOpts, + MacroBuilder &Builder) { + Builder.defineMacro("_GLIBCXX_PREDEFINED_OBJC_ARC_IS_SCALAR"); + + std::string Result; + { + // Provide specializations for the __is_scalar type trait so that + // lifetime-qualified objects are not considered "scalar" types, which + // libstdc++ uses as an indicator of the presence of trivial copy, assign, + // default-construct, and destruct semantics (none of which hold for + // lifetime-qualified objects in ARC). + llvm::raw_string_ostream Out(Result); + + Out << "namespace std {\n" + << "\n" + << "struct __true_type;\n" + << "struct __false_type;\n" + << "\n"; + + Out << "template<typename _Tp> struct __is_scalar;\n" + << "\n"; + + Out << "template<typename _Tp>\n" + << "struct __is_scalar<__attribute__((objc_lifetime(strong))) _Tp> {\n" + << " enum { __value = 0 };\n" + << " typedef __false_type __type;\n" + << "};\n" + << "\n"; + + if (!LangOpts.ObjCNoAutoRefCountRuntime) { + Out << "template<typename _Tp>\n" + << "struct __is_scalar<__attribute__((objc_lifetime(weak))) _Tp> {\n" + << " enum { __value = 0 };\n" + << " typedef __false_type __type;\n" + << "};\n" + << "\n"; + } + + Out << "template<typename _Tp>\n" + << "struct __is_scalar<__attribute__((objc_lifetime(autoreleasing)))" + << " _Tp> {\n" + << " enum { __value = 0 };\n" + << " typedef __false_type __type;\n" + << "};\n" + << "\n"; + + Out << "}\n"; + } + Builder.append(Result); +} + static void InitializeStandardPredefinedMacros(const TargetInfo &TI, const LangOptions &LangOpts, const FrontendOptions &FEOpts, @@ -560,6 +679,7 @@ void clang::InitializePreprocessor(Preprocessor &PP, const PreprocessorOptions &InitOpts, const HeaderSearchOptions &HSOpts, const FrontendOptions &FEOpts) { + const LangOptions &LangOpts = PP.getLangOptions(); std::string PredefineBuffer; PredefineBuffer.reserve(4080); llvm::raw_string_ostream Predefines(PredefineBuffer); @@ -575,10 +695,27 @@ void clang::InitializePreprocessor(Preprocessor &PP, Builder.append("# 1 \"<built-in>\" 3"); // Install things like __POWERPC__, __GNUC__, etc into the macro table. - if (InitOpts.UsePredefines) - InitializePredefinedMacros(PP.getTargetInfo(), PP.getLangOptions(), - FEOpts, Builder); - + if (InitOpts.UsePredefines) { + InitializePredefinedMacros(PP.getTargetInfo(), LangOpts, FEOpts, Builder); + + // Install definitions to make Objective-C++ ARC work well with various + // C++ Standard Library implementations. + if (LangOpts.ObjC1 && LangOpts.CPlusPlus && LangOpts.ObjCAutoRefCount) { + switch (InitOpts.ObjCXXARCStandardLibrary) { + case ARCXX_nolib: + break; + + case ARCXX_libcxx: + AddObjCXXARCLibcxxDefines(LangOpts, Builder); + break; + + case ARCXX_libstdcxx: + AddObjCXXARCLibstdcxxDefines(LangOpts, Builder); + break; + } + } + } + // Even with predefines off, some macros are still predefined. // These should all be defined in the preprocessor according to the // current language configuration. diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 01cd75fa848..9e8533a4484 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -551,6 +551,11 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("cxx_exceptions", LangOpts.Exceptions) .Case("cxx_rtti", LangOpts.RTTI) .Case("enumerator_attributes", true) + // Objective-C features + .Case("objc_arr", LangOpts.ObjCAutoRefCount) // FIXME: REMOVE? + .Case("objc_arc", LangOpts.ObjCAutoRefCount) + .Case("objc_arc_weak", LangOpts.ObjCAutoRefCount && + !LangOpts.ObjCNoAutoRefCountRuntime) .Case("objc_nonfragile_abi", LangOpts.ObjCNonFragileABI) .Case("objc_weak_class", LangOpts.ObjCNonFragileABI) .Case("ownership_holds", true) diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index ad3fcfe0d36..2d3fcf83403 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -31,9 +31,11 @@ using namespace clang; /// /// Called type-id in C++. TypeResult Parser::ParseTypeName(SourceRange *Range, - Declarator::TheContext Context) { + Declarator::TheContext Context, + ObjCDeclSpec *objcQuals) { // Parse the common declaration-specifiers piece. DeclSpec DS(AttrFactory); + DS.setObjCQualifiers(objcQuals); ParseSpecifierQualifierList(DS); // Parse the abstract-declarator, if present. diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index d349220e3d1..01bd0ed336e 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1740,7 +1740,12 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() { /// '(' type-name ')' '{' initializer-list ',' '}' /// cast-expression: [C99 6.5.4] /// '(' type-name ')' cast-expression -/// +/// [ARC] bridged-cast-expression +/// +/// [ARC] bridged-cast-expression: +/// (__bridge type-name) cast-expression +/// (__bridge_transfer type-name) cast-expression +/// (__bridge_retained type-name) cast-expression ExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, ParsedType TypeOfCast, ParsedType &CastTy, @@ -1772,7 +1777,30 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, // If the substmt parsed correctly, build the AST node. if (!Stmt.isInvalid()) Result = Actions.ActOnStmtExpr(OpenLoc, Stmt.take(), Tok.getLocation()); - + } else if (ExprType >= CompoundLiteral && + (Tok.is(tok::kw___bridge) || + Tok.is(tok::kw___bridge_transfer) || + Tok.is(tok::kw___bridge_retained))) { + // Parse an Objective-C ARC ownership cast expression. + ObjCBridgeCastKind Kind; + if (Tok.is(tok::kw___bridge)) + Kind = OBC_Bridge; + else if (Tok.is(tok::kw___bridge_transfer)) + Kind = OBC_BridgeTransfer; + else + Kind = OBC_BridgeRetained; + + SourceLocation BridgeKeywordLoc = ConsumeToken(); + TypeResult Ty = ParseTypeName(); + SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, OpenLoc); + ExprResult SubExpr = ParseCastExpression(false, false, ParsedType()); + + if (Ty.isInvalid() || SubExpr.isInvalid()) + return ExprError(); + + return Actions.ActOnObjCBridgedCast(getCurScope(), OpenLoc, Kind, + BridgeKeywordLoc, Ty.get(), + RParenLoc, SubExpr.get()); } else if (ExprType >= CompoundLiteral && isTypeIdInParens(isAmbiguousTypeId)) { diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index a8c18c01a29..4f28321827a 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -480,6 +480,10 @@ void Parser::ParseObjCInterfaceDeclList(Decl *interfaceDecl, /// retain /// copy /// nonatomic +/// atomic +/// strong +/// weak +/// unsafe_unretained /// void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS, Decl *ClassDecl) { assert(Tok.getKind() == tok::l_paren); @@ -504,16 +508,22 @@ void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS, Decl *ClassDecl) { DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_readonly); else if (II->isStr("assign")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_assign); + else if (II->isStr("unsafe_unretained")) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_unsafe_unretained); else if (II->isStr("readwrite")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_readwrite); else if (II->isStr("retain")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_retain); + else if (II->isStr("strong")) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_strong); else if (II->isStr("copy")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_copy); else if (II->isStr("nonatomic")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nonatomic); else if (II->isStr("atomic")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_atomic); + else if (II->isStr("weak")) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_weak); else if (II->isStr("getter") || II->isStr("setter")) { bool IsSetter = II->getNameStart()[0] == 's'; @@ -775,11 +785,12 @@ ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS, ParsedType Ty; if (isTypeSpecifierQualifier()) { - TypeResult TypeSpec = ParseTypeName(0, Declarator::ObjCPrototypeContext); + TypeResult TypeSpec = + ParseTypeName(0, Declarator::ObjCPrototypeContext, &DS); if (!TypeSpec.isInvalid()) Ty = TypeSpec.get(); } - + if (Tok.is(tok::r_paren)) ConsumeParen(); else if (Tok.getLocation() == TypeStartLoc) { @@ -853,6 +864,7 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, } // Now parse the selector. + SourceLocation SelectorStartLoc = Tok.getLocation(); SourceLocation selLoc; IdentifierInfo *SelIdent = ParseObjCSelectorPiece(selLoc); @@ -1690,6 +1702,29 @@ StmtResult Parser::ParseObjCTryStmt(SourceLocation atLoc) { FinallyStmt.take()); } +/// objc-autoreleasepool-statement: +/// @autoreleasepool compound-statement +/// +StmtResult +Parser::ParseObjCAutoreleasePoolStmt(SourceLocation atLoc) { + ConsumeToken(); // consume autoreleasepool + if (Tok.isNot(tok::l_brace)) { + Diag(Tok, diag::err_expected_lbrace); + return StmtError(); + } + // Enter a scope to hold everything within the compound stmt. Compound + // statements can always hold declarations. + ParseScope BodyScope(this, Scope::DeclScope); + + StmtResult AutoreleasePoolBody(ParseCompoundStatementBody()); + + BodyScope.Exit(); + if (AutoreleasePoolBody.isInvalid()) + AutoreleasePoolBody = Actions.ActOnNullStmt(Tok.getLocation()); + return Actions.ActOnObjCAutoreleasePoolStmt(atLoc, + AutoreleasePoolBody.take()); +} + /// objc-method-def: objc-method-proto ';'[opt] '{' body '}' /// Decl *Parser::ParseObjCMethodDefinition() { @@ -1765,6 +1800,9 @@ StmtResult Parser::ParseObjCAtStatement(SourceLocation AtLoc) { if (Tok.isObjCAtKeyword(tok::objc_synchronized)) return ParseObjCSynchronizedStmt(AtLoc); + + if (Tok.isObjCAtKeyword(tok::objc_autoreleasepool)) + return ParseObjCAutoreleasePoolStmt(AtLoc); ExprResult Res(ParseExpressionWithLeadingAt(AtLoc)); if (Res.isInvalid()) { diff --git a/clang/lib/Rewrite/FixItRewriter.cpp b/clang/lib/Rewrite/FixItRewriter.cpp index 8dcc5dcba78..e50793e2775 100644 --- a/clang/lib/Rewrite/FixItRewriter.cpp +++ b/clang/lib/Rewrite/FixItRewriter.cpp @@ -121,8 +121,7 @@ void FixItRewriter::HandleDiagnostic(Diagnostic::Level DiagLevel, if (Hint.CodeToInsert.empty()) { // We're removing code. - if (Rewrite.RemoveText(Hint.RemoveRange.getBegin(), - Rewrite.getRangeSize(Hint.RemoveRange))) + if (Rewrite.RemoveText(Hint.RemoveRange)) Failed = true; continue; } diff --git a/clang/lib/Rewrite/Rewriter.cpp b/clang/lib/Rewrite/Rewriter.cpp index 51fe379fee0..92f516074ec 100644 --- a/clang/lib/Rewrite/Rewriter.cpp +++ b/clang/lib/Rewrite/Rewriter.cpp @@ -231,10 +231,44 @@ RewriteBuffer &Rewriter::getEditBuffer(FileID FID) { /// InsertText - Insert the specified string at the specified location in the /// original buffer. bool Rewriter::InsertText(SourceLocation Loc, llvm::StringRef Str, - bool InsertAfter) { + bool InsertAfter, bool indentNewLines) { + using llvm::StringRef; + if (!isRewritable(Loc)) return true; FileID FID; unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID); + + llvm::SmallString<128> indentedStr; + if (indentNewLines && Str.find('\n') != StringRef::npos) { + StringRef MB = SourceMgr->getBufferData(FID); + + unsigned lineNo = SourceMgr->getLineNumber(FID, StartOffs) - 1; + const SrcMgr::ContentCache * + Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache(); + unsigned lineOffs = Content->SourceLineCache[lineNo]; + + // Find the whitespace at the start of the line. + StringRef indentSpace; + { + unsigned i = lineOffs; + while (isWhitespace(MB[i])) + ++i; + indentSpace = MB.substr(lineOffs, i-lineOffs); + } + + llvm::SmallVector<StringRef, 4> lines; + Str.split(lines, "\n"); + + for (unsigned i = 0, e = lines.size(); i != e; ++i) { + indentedStr += lines[i]; + if (i < e-1) { + indentedStr += '\n'; + indentedStr += indentSpace; + } + } + Str = indentedStr.str(); + } + getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter); return false; } @@ -317,6 +351,7 @@ bool Rewriter::IncreaseIndentation(CharSourceRange range, SourceLocation parentIndent) { using llvm::StringRef; + if (range.isInvalid()) return true; if (!isRewritable(range.getBegin())) return true; if (!isRewritable(range.getEnd())) return true; if (!isRewritable(parentIndent)) return true; @@ -330,7 +365,7 @@ bool Rewriter::IncreaseIndentation(CharSourceRange range, if (StartFileID != EndFileID || StartFileID != parentFileID) return true; - if (StartOff >= EndOff || parentOff >= StartOff) + if (StartOff > EndOff) return true; FileID FID = StartFileID; @@ -343,16 +378,12 @@ bool Rewriter::IncreaseIndentation(CharSourceRange range, const SrcMgr::ContentCache * Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache(); - // Find where the line starts for the three offsets. + // Find where the lines start. unsigned parentLineOffs = Content->SourceLineCache[parentLineNo]; unsigned startLineOffs = Content->SourceLineCache[startLineNo]; - unsigned endLineOffs = Content->SourceLineCache[endLineNo]; - - if (startLineOffs == endLineOffs || startLineOffs == parentLineOffs) - return true; // Find the whitespace at the start of each line. - StringRef parentSpace, startSpace, endSpace; + StringRef parentSpace, startSpace; { unsigned i = parentLineOffs; while (isWhitespace(MB[i])) @@ -363,11 +394,6 @@ bool Rewriter::IncreaseIndentation(CharSourceRange range, while (isWhitespace(MB[i])) ++i; startSpace = MB.substr(startLineOffs, i-startLineOffs); - - i = endLineOffs; - while (isWhitespace(MB[i])) - ++i; - endSpace = MB.substr(endLineOffs, i-endLineOffs); } if (parentSpace.size() >= startSpace.size()) return true; @@ -378,19 +404,14 @@ bool Rewriter::IncreaseIndentation(CharSourceRange range, // Indent the lines between start/end offsets. RewriteBuffer &RB = getEditBuffer(FID); - for (unsigned i = startLineOffs; i != endLineOffs; ++i) { - if (MB[i] == '\n') { - unsigned startOfLine = i+1; - if (startOfLine == endLineOffs) - break; - StringRef origIndent; - unsigned ws = startOfLine; - while (isWhitespace(MB[ws])) - ++ws; - origIndent = MB.substr(startOfLine, ws-startOfLine); - if (origIndent.startswith(startSpace)) - RB.InsertText(startOfLine, indent, /*InsertAfter=*/false); - } + for (unsigned lineNo = startLineNo; lineNo <= endLineNo; ++lineNo) { + unsigned offs = Content->SourceLineCache[lineNo]; + unsigned i = offs; + while (isWhitespace(MB[i])) + ++i; + StringRef origIndent = MB.substr(offs, i-offs); + if (origIndent.startswith(startSpace)) + RB.InsertText(offs, indent, /*InsertAfter=*/false); } return false; diff --git a/clang/lib/Sema/AttributeList.cpp b/clang/lib/Sema/AttributeList.cpp index 619a5b961bf..fda4e0ccd85 100644 --- a/clang/lib/Sema/AttributeList.cpp +++ b/clang/lib/Sema/AttributeList.cpp @@ -177,6 +177,11 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) { .Case("cf_consumed", AT_cf_consumed) .Case("cf_returns_not_retained", AT_cf_returns_not_retained) .Case("cf_returns_retained", AT_cf_returns_retained) + .Case("cf_returns_autoreleased", AT_cf_returns_autoreleased) + .Case("ns_consumes_self", AT_ns_consumes_self) + .Case("ns_consumed", AT_ns_consumed) + .Case("objc_lifetime", AT_objc_lifetime) + .Case("objc_precise_lifetime", AT_objc_precise_lifetime) .Case("ownership_returns", AT_ownership_returns) .Case("ownership_holds", AT_ownership_holds) .Case("ownership_takes", AT_ownership_takes) diff --git a/clang/lib/Sema/DelayedDiagnostic.cpp b/clang/lib/Sema/DelayedDiagnostic.cpp index af548fe1346..c6744ed80d4 100644 --- a/clang/lib/Sema/DelayedDiagnostic.cpp +++ b/clang/lib/Sema/DelayedDiagnostic.cpp @@ -47,5 +47,8 @@ void DelayedDiagnostic::Destroy() { case Deprecation: delete [] DeprecationData.Message; break; + + case ForbiddenType: + break; } } diff --git a/clang/lib/Sema/JumpDiagnostics.cpp b/clang/lib/Sema/JumpDiagnostics.cpp index 679f4fefa22..007d755a877 100644 --- a/clang/lib/Sema/JumpDiagnostics.cpp +++ b/clang/lib/Sema/JumpDiagnostics.cpp @@ -111,90 +111,110 @@ unsigned JumpScopeChecker::GetDeepestCommonScope(unsigned A, unsigned B) { return A; } +typedef std::pair<unsigned,unsigned> ScopePair; + /// GetDiagForGotoScopeDecl - If this decl induces a new goto scope, return a /// diagnostic that should be emitted if control goes over it. If not, return 0. -static std::pair<unsigned,unsigned> - GetDiagForGotoScopeDecl(const Decl *D, bool isCPlusPlus) { +static ScopePair GetDiagForGotoScopeDecl(ASTContext &Context, const Decl *D) { if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { unsigned InDiag = 0, OutDiag = 0; if (VD->getType()->isVariablyModifiedType()) InDiag = diag::note_protected_by_vla; - if (VD->hasAttr<BlocksAttr>()) { - InDiag = diag::note_protected_by___block; - OutDiag = diag::note_exits___block; - } else if (VD->hasAttr<CleanupAttr>()) { - InDiag = diag::note_protected_by_cleanup; - OutDiag = diag::note_exits_cleanup; - } else if (isCPlusPlus) { - if (!VD->hasLocalStorage()) - return std::make_pair(InDiag, OutDiag); - - ASTContext &Context = D->getASTContext(); - QualType T = Context.getBaseElementType(VD->getType()); - if (!T->isDependentType()) { - // C++0x [stmt.dcl]p3: - // A program that jumps from a point where a variable with automatic - // storage duration is not in scope to a point where it is in scope - // is ill-formed unless the variable has scalar type, class type with - // a trivial default constructor and a trivial destructor, a - // cv-qualified version of one of these types, or an array of one of - // the preceding types and is declared without an initializer (8.5). - // Check whether this is a C++ class. - CXXRecordDecl *Record = T->getAsCXXRecordDecl(); - - if (const Expr *Init = VD->getInit()) { - bool CallsTrivialConstructor = false; - if (Record) { - // FIXME: With generalized initializer lists, this may - // classify "X x{};" as having no initializer. - if (const CXXConstructExpr *Construct - = dyn_cast<CXXConstructExpr>(Init)) - if (const CXXConstructorDecl *Constructor - = Construct->getConstructor()) - if ((Context.getLangOptions().CPlusPlus0x - ? Record->hasTrivialDefaultConstructor() - : Record->isPOD()) && - Constructor->isDefaultConstructor()) - CallsTrivialConstructor = true; - - if (CallsTrivialConstructor && !Record->hasTrivialDestructor()) - InDiag = diag::note_protected_by_variable_nontriv_destructor; + if (VD->hasAttr<BlocksAttr>()) + return ScopePair(diag::note_protected_by___block, + diag::note_exits___block); + + if (VD->hasAttr<CleanupAttr>()) + return ScopePair(diag::note_protected_by_cleanup, + diag::note_exits_cleanup); + + if (Context.getLangOptions().ObjCAutoRefCount && VD->hasLocalStorage()) { + switch (VD->getType().getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + break; + + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Weak: + return ScopePair(diag::note_protected_by_objc_lifetime, + diag::note_exits_objc_lifetime); + } + } + + if (Context.getLangOptions().CPlusPlus && VD->hasLocalStorage()) { + // C++0x [stmt.dcl]p3: + // A program that jumps from a point where a variable with automatic + // storage duration is not in scope to a point where it is in scope + // is ill-formed unless the variable has scalar type, class type with + // a trivial default constructor and a trivial destructor, a + // cv-qualified version of one of these types, or an array of one of + // the preceding types and is declared without an initializer. + + // C++03 [stmt.dcl.p3: + // A program that jumps from a point where a local variable + // with automatic storage duration is not in scope to a point + // where it is in scope is ill-formed unless the variable has + // POD type and is declared without an initializer. + + if (const Expr *init = VD->getInit()) { + // We actually give variables of record type (or array thereof) + // an initializer even if that initializer only calls a trivial + // ctor. Detect that case. + // FIXME: With generalized initializer lists, this may + // classify "X x{};" as having no initializer. + unsigned inDiagToUse = diag::note_protected_by_variable_init; + + const CXXRecordDecl *record = 0; + + if (const CXXConstructExpr *cce = dyn_cast<CXXConstructExpr>(init)) { + const CXXConstructorDecl *ctor = cce->getConstructor(); + record = ctor->getParent(); + + if (ctor->isTrivial() && ctor->isDefaultConstructor()) { + if (Context.getLangOptions().CPlusPlus0x) { + inDiagToUse = (record->hasTrivialDestructor() ? 0 : + diag::note_protected_by_variable_nontriv_destructor); + } else { + if (record->isPOD()) + inDiagToUse = 0; + } } - - if (!CallsTrivialConstructor) - InDiag = diag::note_protected_by_variable_init; + } else if (VD->getType()->isArrayType()) { + record = VD->getType()->getBaseElementTypeUnsafe() + ->getAsCXXRecordDecl(); } - - // Note whether we have a class with a non-trivial destructor. - if (Record && !Record->hasTrivialDestructor()) + + if (inDiagToUse) + InDiag = inDiagToUse; + + // Also object to indirect jumps which leave scopes with dtors. + if (record && !record->hasTrivialDestructor()) OutDiag = diag::note_exits_dtor; } } - return std::make_pair(InDiag, OutDiag); + return ScopePair(InDiag, OutDiag); } if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) { if (TD->getUnderlyingType()->isVariablyModifiedType()) - return std::make_pair((unsigned) diag::note_protected_by_vla_typedef, 0); + return ScopePair(diag::note_protected_by_vla_typedef, 0); } if (const TypeAliasDecl *TD = dyn_cast<TypeAliasDecl>(D)) { if (TD->getUnderlyingType()->isVariablyModifiedType()) - return std::make_pair((unsigned) diag::note_protected_by_vla_type_alias, 0); + return ScopePair(diag::note_protected_by_vla_type_alias, 0); } - return std::make_pair(0U, 0U); + return ScopePair(0U, 0U); } /// \brief Build scope information for a declaration that is part of a DeclStmt. void JumpScopeChecker::BuildScopeInformation(Decl *D, unsigned &ParentScope) { - bool isCPlusPlus = this->S.getLangOptions().CPlusPlus; - // If this decl causes a new scope, push and switch to it. - std::pair<unsigned,unsigned> Diags - = GetDiagForGotoScopeDecl(D, isCPlusPlus); + std::pair<unsigned,unsigned> Diags = GetDiagForGotoScopeDecl(S.Context, D); if (Diags.first || Diags.second) { Scopes.push_back(GotoScope(ParentScope, Diags.first, Diags.second, D->getLocation())); @@ -369,6 +389,18 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) { continue; } + // Disallow jumps into the protected statement of an @autoreleasepool. + if (ObjCAutoreleasePoolStmt *AS = dyn_cast<ObjCAutoreleasePoolStmt>(SubStmt)){ + // Recursively walk the AST for the @autoreleasepool part, protected by a new + // scope. + Scopes.push_back(GotoScope(ParentScope, + diag::note_protected_by_objc_autoreleasepool, + diag::note_exits_objc_autoreleasepool, + AS->getAtLoc())); + BuildScopeInformation(AS->getSubStmt(), Scopes.size()-1); + continue; + } + // Recursively walk the AST. BuildScopeInformation(SubStmt, ParentScope); } diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 8297b3155a8..7d22031ecbc 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -143,7 +143,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()), ExternalSource(0), CodeCompleter(CodeCompleter), CurContext(0), PackContext(0), MSStructPragmaOn(false), VisContext(0), - LateTemplateParser(0), OpaqueParser(0), + ExprNeedsCleanups(0), LateTemplateParser(0), OpaqueParser(0), IdResolver(pp.getLangOptions()), CXXTypeInfoDecl(0), MSVCGuidDecl(0), GlobalNewDeleteDeclared(false), CompleteTranslationUnit(CompleteTranslationUnit), @@ -162,7 +162,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, &Context); ExprEvalContexts.push_back( - ExpressionEvaluationContextRecord(PotentiallyEvaluated, 0)); + ExpressionEvaluationContextRecord(PotentiallyEvaluated, 0, false)); FunctionScopes.push_back(new FunctionScopeInfo(Diags)); } @@ -202,6 +202,32 @@ Sema::~Sema() { ExternalSema->ForgetSema(); } + +/// makeUnavailableInSystemHeader - There is an error in the current +/// context. If we're still in a system header, and we can plausibly +/// make the relevant declaration unavailable instead of erroring, do +/// so and return true. +bool Sema::makeUnavailableInSystemHeader(SourceLocation loc, + llvm::StringRef msg) { + // If we're not in a function, it's an error. + FunctionDecl *fn = dyn_cast<FunctionDecl>(CurContext); + if (!fn) return false; + + // If we're in template instantiation, it's an error. + if (!ActiveTemplateInstantiations.empty()) + return false; + + // If that function's not in a system header, it's an error. + if (!Context.getSourceManager().isInSystemHeader(loc)) + return false; + + // If the function is already unavailable, it's not an error. + if (fn->hasAttr<UnavailableAttr>()) return true; + + fn->addAttr(new (Context) UnavailableAttr(loc, Context, msg)); + return true; +} + ASTMutationListener *Sema::getASTMutationListener() const { return getASTConsumer().GetASTMutationListener(); } @@ -211,13 +237,17 @@ ASTMutationListener *Sema::getASTMutationListener() const { /// The result is of the given category. ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty, CastKind Kind, ExprValueKind VK, - const CXXCastPath *BasePath) { + const CXXCastPath *BasePath, + CheckedConversionKind CCK) { QualType ExprTy = Context.getCanonicalType(E->getType()); QualType TypeTy = Context.getCanonicalType(Ty); if (ExprTy == TypeTy) return Owned(E); + if (getLangOptions().ObjCAutoRefCount) + CheckObjCARCConversion(SourceRange(), Ty, E, CCK); + // If this is a derived-to-base cast to a through a virtual base, we // need a vtable. if (Kind == CK_DerivedToBase && @@ -729,8 +759,8 @@ void Sema::PopFunctionOrBlockScope(const AnalysisBasedWarnings::Policy *WP, /// \brief Determine whether any errors occurred within this function/method/ /// block. -bool Sema::hasAnyErrorsInThisFunction() const { - return getCurFunction()->ErrorTrap.hasErrorOccurred(); +bool Sema::hasAnyUnrecoverableErrorsInThisFunction() const { + return getCurFunction()->ErrorTrap.hasUnrecoverableErrorOccurred(); } BlockScopeInfo *Sema::getCurBlock() { diff --git a/clang/lib/Sema/SemaCXXCast.cpp b/clang/lib/Sema/SemaCXXCast.cpp index e46ad5bcfeb..0b3083aaa62 100644 --- a/clang/lib/Sema/SemaCXXCast.cpp +++ b/clang/lib/Sema/SemaCXXCast.cpp @@ -63,7 +63,8 @@ static void CheckDynamicCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, CastKind &Kind, CXXCastPath &BasePath); -static bool CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType); +static bool CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType, + bool CheckCVR, bool CheckObjCLifetime); // The Try functions attempt a specific way of casting. If they succeed, they // return TC_Success. If their way of casting is not appropriate for the given @@ -109,12 +110,14 @@ static TryCastResult TryStaticMemberPointerUpcast(Sema &Self, ExprResult &SrcExp CXXCastPath &BasePath); static TryCastResult TryStaticImplicitCast(Sema &Self, ExprResult &SrcExpr, - QualType DestType, bool CStyle, + QualType DestType, + Sema::CheckedConversionKind CCK, const SourceRange &OpRange, unsigned &msg, CastKind &Kind); static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr, - QualType DestType, bool CStyle, + QualType DestType, + Sema::CheckedConversionKind CCK, const SourceRange &OpRange, unsigned &msg, CastKind &Kind, @@ -248,8 +251,10 @@ static bool tryDiagnoseOverloadedCast(Sema &S, CastType CT, InitializedEntity entity = InitializedEntity::InitializeTemporary(destType); InitializationKind initKind - = InitializationKind::CreateCast(/*type range?*/ range, - (CT == CT_CStyle || CT == CT_Functional)); + = (CT == CT_CStyle)? InitializationKind::CreateCStyleCast(range.getBegin(), + range) + : (CT == CT_Functional)? InitializationKind::CreateFunctionalCast(range) + : InitializationKind::CreateCast(/*type range?*/ range); InitializationSequence sequence(S, entity, initKind, &src, 1); assert(sequence.Failed() && "initialization succeeded on second try?"); @@ -373,8 +378,19 @@ static bool UnwrapDissimilarPointerTypes(QualType& T1, QualType& T2) { /// DestType casts away constness as defined in C++ 5.2.11p8ff. This is used by /// the cast checkers. Both arguments must denote pointer (possibly to member) /// types. +/// +/// \param CheckCVR Whether to check for const/volatile/restrict qualifiers. +/// +/// \param CheckObjCLifetime Whether to check Objective-C lifetime qualifiers. static bool -CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType) { +CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType, + bool CheckCVR, bool CheckObjCLifetime) { + // If the only checking we care about is for Objective-C lifetime qualifiers, + // and we're not in ARC mode, there's nothing to check. + if (!CheckCVR && CheckObjCLifetime && + !Self.Context.getLangOptions().ObjCAutoRefCount) + return false; + // Casting away constness is defined in C++ 5.2.11p8 with reference to // C++ 4.4. We piggyback on Sema::IsQualificationConversion for this, since // the rules are non-trivial. So first we construct Tcv *...cv* as described @@ -394,13 +410,23 @@ CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType) { // purpose of this check, because other qualifiers (address spaces, // Objective-C GC, etc.) are part of the type's identity. while (UnwrapDissimilarPointerTypes(UnwrappedSrcType, UnwrappedDestType)) { - Qualifiers SrcQuals; + // Determine the relevant qualifiers at this level. + Qualifiers SrcQuals, DestQuals; Self.Context.getUnqualifiedArrayType(UnwrappedSrcType, SrcQuals); - cv1.push_back(Qualifiers::fromCVRMask(SrcQuals.getCVRQualifiers())); - - Qualifiers DestQuals; Self.Context.getUnqualifiedArrayType(UnwrappedDestType, DestQuals); - cv2.push_back(Qualifiers::fromCVRMask(DestQuals.getCVRQualifiers())); + + Qualifiers RetainedSrcQuals, RetainedDestQuals; + if (CheckCVR) { + RetainedSrcQuals.setCVRQualifiers(SrcQuals.getCVRQualifiers()); + RetainedDestQuals.setCVRQualifiers(DestQuals.getCVRQualifiers()); + } + + if (CheckObjCLifetime && + !DestQuals.compatiblyIncludesObjCLifetime(SrcQuals)) + return true; + + cv1.push_back(RetainedSrcQuals); + cv2.push_back(RetainedDestQuals); } if (cv1.empty()) return false; @@ -420,8 +446,10 @@ CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType) { } // Test if they're compatible. + bool ObjCLifetimeConversion; return SrcConstruct != DestConstruct && - !Self.IsQualificationConversion(SrcConstruct, DestConstruct, false); + !Self.IsQualificationConversion(SrcConstruct, DestConstruct, false, + ObjCLifetimeConversion); } /// CheckDynamicCast - Check that a dynamic_cast\<DestType\>(SrcExpr) is valid. @@ -595,9 +623,10 @@ CheckReinterpretCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, } unsigned msg = diag::err_bad_cxx_cast_generic; - if (TryReinterpretCast(Self, SrcExpr, DestType, /*CStyle*/false, OpRange, - msg, Kind) - != TC_Success && msg != 0) + TryCastResult tcr = + TryReinterpretCast(Self, SrcExpr, DestType, + /*CStyle*/false, OpRange, msg, Kind); + if (tcr != TC_Success && msg != 0) { if (SrcExpr.isInvalid()) // if conversion failed, don't report another error return; @@ -611,7 +640,10 @@ CheckReinterpretCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, } else { diagnoseBadCast(Self, msg, CT_Reinterpret, OpRange, SrcExpr.get(), DestType); } - } + } else if (tcr == TC_Success && Self.getLangOptions().ObjCAutoRefCount) { + Self.CheckObjCARCConversion(OpRange, DestType, + SrcExpr.get(), Sema::CCK_OtherCast); + } } @@ -654,8 +686,10 @@ CheckStaticCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, } unsigned msg = diag::err_bad_cxx_cast_generic; - if (TryStaticCast(Self, SrcExpr, DestType, /*CStyle*/false, OpRange, msg, - Kind, BasePath) != TC_Success && msg != 0) { + TryCastResult tcr + = TryStaticCast(Self, SrcExpr, DestType, Sema::CCK_OtherCast, OpRange, msg, + Kind, BasePath); + if (tcr != TC_Success && msg != 0) { if (SrcExpr.isInvalid()) return; if (SrcExpr.get()->getType() == Self.Context.OverloadTy) { @@ -667,6 +701,12 @@ CheckStaticCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, } else { diagnoseBadCast(Self, msg, CT_Static, OpRange, SrcExpr.get(), DestType); } + } else if (tcr == TC_Success) { + if (Kind == CK_BitCast) + Self.CheckCastAlign(SrcExpr.get(), DestType, OpRange); + if (Self.getLangOptions().ObjCAutoRefCount) + Self.CheckObjCARCConversion(OpRange, DestType, + SrcExpr.get(), Sema::CCK_OtherCast); } else if (Kind == CK_BitCast) Self.CheckCastAlign(SrcExpr.get(), DestType, OpRange); @@ -676,10 +716,15 @@ CheckStaticCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, /// possible. If @p CStyle, ignore access restrictions on hierarchy casting /// and casting away constness. static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr, - QualType DestType, bool CStyle, + QualType DestType, + Sema::CheckedConversionKind CCK, const SourceRange &OpRange, unsigned &msg, CastKind &Kind, CXXCastPath &BasePath) { + // Determine whether we have the semantics of a C-style cast. + bool CStyle + = (CCK == Sema::CCK_CStyleCast || CCK == Sema::CCK_FunctionalCast); + // The order the tests is not entirely arbitrary. There is one conversion // that can be handled in two different ways. Given: // struct A {}; @@ -715,7 +760,7 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr, // C++ 5.2.9p2: An expression e can be explicitly converted to a type T // [...] if the declaration "T t(e);" is well-formed, [...]. - tcr = TryStaticImplicitCast(Self, SrcExpr, DestType, CStyle, OpRange, msg, + tcr = TryStaticImplicitCast(Self, SrcExpr, DestType, CCK, OpRange, msg, Kind); if (SrcExpr.isInvalid()) return TC_Failed; @@ -792,10 +837,20 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr, QualType DestPointee = DestPointer->getPointeeType(); if (DestPointee->isIncompleteOrObjectType()) { // This is definitely the intended conversion, but it might fail due - // to a const violation. - if (!CStyle && !DestPointee.isAtLeastAsQualifiedAs(SrcPointee)) { - msg = diag::err_bad_cxx_cast_qualifiers_away; - return TC_Failed; + // to a qualifier violation. Note that we permit Objective-C lifetime + // and GC qualifier mismatches here. + if (!CStyle) { + Qualifiers DestPointeeQuals = DestPointee.getQualifiers(); + Qualifiers SrcPointeeQuals = SrcPointee.getQualifiers(); + DestPointeeQuals.removeObjCGCAttr(); + DestPointeeQuals.removeObjCLifetime(); + SrcPointeeQuals.removeObjCGCAttr(); + SrcPointeeQuals.removeObjCLifetime(); + if (DestPointeeQuals != SrcPointeeQuals && + !DestPointeeQuals.compatiblyIncludes(SrcPointeeQuals)) { + msg = diag::err_bad_cxx_cast_qualifiers_away; + return TC_Failed; + } } Kind = CK_BitCast; return TC_Success; @@ -845,6 +900,7 @@ TryLValueToRValueCast(Sema &Self, Expr *SrcExpr, QualType DestType, // FIXME: Should allow casting away constness if CStyle. bool DerivedToBase; bool ObjCConversion; + bool ObjCLifetimeConversion; QualType FromType = SrcExpr->getType(); QualType ToType = R->getPointeeType(); if (CStyle) { @@ -854,8 +910,9 @@ TryLValueToRValueCast(Sema &Self, Expr *SrcExpr, QualType DestType, if (Self.CompareReferenceRelationship(SrcExpr->getLocStart(), ToType, FromType, - DerivedToBase, ObjCConversion) < - Sema::Ref_Compatible_With_Added_Qualification) { + DerivedToBase, ObjCConversion, + ObjCLifetimeConversion) + < Sema::Ref_Compatible_With_Added_Qualification) { msg = diag::err_bad_lvalue_to_rvalue_cast; return TC_Failed; } @@ -1172,7 +1229,8 @@ TryStaticMemberPointerUpcast(Sema &Self, ExprResult &SrcExpr, QualType SrcType, /// @c static_cast if the declaration "T t(e);" is well-formed [...]. TryCastResult TryStaticImplicitCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, - bool CStyle, const SourceRange &OpRange, unsigned &msg, + Sema::CheckedConversionKind CCK, + const SourceRange &OpRange, unsigned &msg, CastKind &Kind) { if (DestType->isRecordType()) { if (Self.RequireCompleteType(OpRange.getBegin(), DestType, @@ -1184,7 +1242,11 @@ TryStaticImplicitCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, InitializedEntity Entity = InitializedEntity::InitializeTemporary(DestType); InitializationKind InitKind - = InitializationKind::CreateCast(/*FIXME:*/OpRange, CStyle); + = (CCK == Sema::CCK_CStyleCast) + ? InitializationKind::CreateCStyleCast(OpRange.getBegin(), OpRange) + : (CCK == Sema::CCK_FunctionalCast) + ? InitializationKind::CreateFunctionalCast(OpRange) + : InitializationKind::CreateCast(OpRange); Expr *SrcExprRaw = SrcExpr.get(); InitializationSequence InitSeq(Self, Entity, InitKind, &SrcExprRaw, 1); @@ -1193,7 +1255,8 @@ TryStaticImplicitCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, // There is no other way that works. // On the other hand, if we're checking a C-style cast, we've still got // the reinterpret_cast way. - + bool CStyle + = (CCK == Sema::CCK_CStyleCast || CCK == Sema::CCK_FunctionalCast); if (InitSeq.Failed() && (CStyle || !DestType->isReferenceType())) return TC_NotApplicable; @@ -1428,7 +1491,8 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, // constness. // A reinterpret_cast followed by a const_cast can, though, so in C-style, // we accept it. - if (!CStyle && CastsAwayConstness(Self, SrcType, DestType)) { + if (CastsAwayConstness(Self, SrcType, DestType, /*CheckCVR=*/!CStyle, + /*CheckObjCLifetime=*/CStyle)) { msg = diag::err_bad_cxx_cast_qualifiers_away; return TC_Failed; } @@ -1543,7 +1607,8 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, // C++ 5.2.10p2: The reinterpret_cast operator shall not cast away constness. // The C-style cast operator can. - if (!CStyle && CastsAwayConstness(Self, SrcType, DestType)) { + if (CastsAwayConstness(Self, SrcType, DestType, /*CheckCVR=*/!CStyle, + /*CheckObjCLifetime=*/CStyle)) { msg = diag::err_bad_cxx_cast_qualifiers_away; return TC_Failed; } @@ -1675,11 +1740,14 @@ Sema::CXXCheckCStyleCast(SourceRange R, QualType CastTy, ExprValueKind &VK, if (tcr == TC_Success) Kind = CK_NoOp; + Sema::CheckedConversionKind CCK + = FunctionalStyle? Sema::CCK_FunctionalCast + : Sema::CCK_CStyleCast; if (tcr == TC_NotApplicable) { // ... or if that is not possible, a static_cast, ignoring const, ... ExprResult CastExprRes = Owned(CastExpr); - tcr = TryStaticCast(*this, CastExprRes, CastTy, /*CStyle*/true, R, msg, - Kind, BasePath); + tcr = TryStaticCast(*this, CastExprRes, CastTy, CCK, R, msg, Kind, + BasePath); if (CastExprRes.isInvalid()) return ExprError(); CastExpr = CastExprRes.take(); @@ -1694,6 +1762,9 @@ Sema::CXXCheckCStyleCast(SourceRange R, QualType CastTy, ExprValueKind &VK, } } + if (getLangOptions().ObjCAutoRefCount && tcr == TC_Success) + CheckObjCARCConversion(R, CastTy, CastExpr, CCK); + if (tcr != TC_Success && msg != 0) { if (CastExpr->getType() == Context.OverloadTy) { DeclAccessPair Found; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 389dd91f320..d45ebf9033d 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -22,6 +22,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" @@ -382,14 +383,14 @@ Sema::SemaBuiltinAtomicOverloaded(ExprResult TheCallResult) { // casts here. // FIXME: We don't allow floating point scalars as input. Expr *FirstArg = TheCall->getArg(0); - if (!FirstArg->getType()->isPointerType()) { + const PointerType *pointerType = FirstArg->getType()->getAs<PointerType>(); + if (!pointerType) { Diag(DRE->getLocStart(), diag::err_atomic_builtin_must_be_pointer) << FirstArg->getType() << FirstArg->getSourceRange(); return ExprError(); } - QualType ValType = - FirstArg->getType()->getAs<PointerType>()->getPointeeType(); + QualType ValType = pointerType->getPointeeType(); if (!ValType->isIntegerType() && !ValType->isAnyPointerType() && !ValType->isBlockPointerType()) { Diag(DRE->getLocStart(), diag::err_atomic_builtin_must_be_pointer_intptr) @@ -397,6 +398,20 @@ Sema::SemaBuiltinAtomicOverloaded(ExprResult TheCallResult) { return ExprError(); } + switch (ValType.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + // okay + break; + + case Qualifiers::OCL_Weak: + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Autoreleasing: + Diag(DRE->getLocStart(), diag::err_arc_atomic_lifetime) + << ValType << FirstArg->getSourceRange(); + return ExprError(); + } + // The majority of builtins return a value, but a few have special return // types, so allow them to override appropriately below. QualType ResultType = ValType; @@ -518,7 +533,8 @@ Sema::SemaBuiltinAtomicOverloaded(ExprResult TheCallResult) { CastKind Kind = CK_Invalid; ExprValueKind VK = VK_RValue; CXXCastPath BasePath; - Arg = CheckCastTypes(Arg.get()->getSourceRange(), ValType, Arg.take(), Kind, VK, BasePath); + Arg = CheckCastTypes(Arg.get()->getLocStart(), Arg.get()->getSourceRange(), + ValType, Arg.take(), Kind, VK, BasePath); if (Arg.isInvalid()) return ExprError(); @@ -1849,6 +1865,7 @@ void Sema::CheckMemsetcpymoveArguments(const CallExpr *Call, QualType DestTy = Dest->getType(); if (const PointerType *DestPtrTy = DestTy->getAs<PointerType>()) { QualType PointeeTy = DestPtrTy->getPointeeType(); + if (PointeeTy->isVoidType()) continue; @@ -1863,16 +1880,22 @@ void Sema::CheckMemsetcpymoveArguments(const CallExpr *Call, break; } + unsigned DiagID; + // Always complain about dynamic classes. - if (isDynamicClassType(PointeeTy)) { - DiagRuntimeBehavior( - Dest->getExprLoc(), Dest, - PDiag(diag::warn_dyn_class_memaccess) - << ArgIdx << FnName << PointeeTy - << Call->getCallee()->getSourceRange()); - } else { + if (isDynamicClassType(PointeeTy)) + DiagID = diag::warn_dyn_class_memaccess; + else if (PointeeTy.hasNonTrivialObjCLifetime() && + !FnName->isStr("memset")) + DiagID = diag::warn_arc_object_memaccess; + else continue; - } + + DiagRuntimeBehavior( + Dest->getExprLoc(), Dest, + PDiag(DiagID) + << ArgIdx << FnName << PointeeTy + << Call->getCallee()->getSourceRange()); DiagRuntimeBehavior( Dest->getExprLoc(), Dest, @@ -1899,7 +1922,8 @@ Sema::CheckReturnStackAddr(Expr *RetValExp, QualType lhsType, // Perform checking for returned stack addresses, local blocks, // label addresses or references to temporaries. - if (lhsType->isPointerType() || lhsType->isBlockPointerType()) { + if (lhsType->isPointerType() || + (!getLangOptions().ObjCAutoRefCount && lhsType->isBlockPointerType())) { stackE = EvalAddr(RetValExp, refVars); } else if (lhsType->isReferenceType()) { stackE = EvalVal(RetValExp, refVars); @@ -2070,7 +2094,8 @@ static Expr *EvalAddr(Expr *E, llvm::SmallVectorImpl<DeclRefExpr *> &refVars) { // pointer values, and pointer-to-pointer conversions. case Stmt::ImplicitCastExprClass: case Stmt::CStyleCastExprClass: - case Stmt::CXXFunctionalCastExprClass: { + case Stmt::CXXFunctionalCastExprClass: + case Stmt::ObjCBridgedCastExprClass: { Expr* SubExpr = cast<CastExpr>(E)->getSubExpr(); QualType T = SubExpr->getType(); @@ -3385,3 +3410,232 @@ void Sema::CheckArrayAccess(const Expr *expr) { } } } + +//===--- CHECK: Objective-C retain cycles ----------------------------------// + +namespace { + struct RetainCycleOwner { + RetainCycleOwner() : Variable(0), Indirect(false) {} + VarDecl *Variable; + SourceRange Range; + SourceLocation Loc; + bool Indirect; + + void setLocsFrom(Expr *e) { + Loc = e->getExprLoc(); + Range = e->getSourceRange(); + } + }; +} + +/// Consider whether capturing the given variable can possibly lead to +/// a retain cycle. +static bool considerVariable(VarDecl *var, Expr *ref, RetainCycleOwner &owner) { + // In ARC, it's captured strongly iff the variable has __strong + // lifetime. In MRR, it's captured strongly if the variable is + // __block and has an appropriate type. + if (var->getType().getObjCLifetime() != Qualifiers::OCL_Strong) + return false; + + owner.Variable = var; + owner.setLocsFrom(ref); + return true; +} + +static bool findRetainCycleOwner(Expr *e, RetainCycleOwner &owner) { + while (true) { + e = e->IgnoreParens(); + if (CastExpr *cast = dyn_cast<CastExpr>(e)) { + switch (cast->getCastKind()) { + case CK_BitCast: + case CK_LValueBitCast: + case CK_LValueToRValue: + e = cast->getSubExpr(); + continue; + + case CK_GetObjCProperty: { + // Bail out if this isn't a strong explicit property. + const ObjCPropertyRefExpr *pre = cast->getSubExpr()->getObjCProperty(); + if (pre->isImplicitProperty()) return false; + ObjCPropertyDecl *property = pre->getExplicitProperty(); + if (!(property->getPropertyAttributes() & + (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_copy | + ObjCPropertyDecl::OBJC_PR_strong)) && + !(property->getPropertyIvarDecl() && + property->getPropertyIvarDecl()->getType() + .getObjCLifetime() == Qualifiers::OCL_Strong)) + return false; + + owner.Indirect = true; + e = const_cast<Expr*>(pre->getBase()); + continue; + } + + default: + return false; + } + } + + if (ObjCIvarRefExpr *ref = dyn_cast<ObjCIvarRefExpr>(e)) { + ObjCIvarDecl *ivar = ref->getDecl(); + if (ivar->getType().getObjCLifetime() != Qualifiers::OCL_Strong) + return false; + + // Try to find a retain cycle in the base. + if (!findRetainCycleOwner(ref->getBase(), owner)) + return false; + + if (ref->isFreeIvar()) owner.setLocsFrom(ref); + owner.Indirect = true; + return true; + } + + if (DeclRefExpr *ref = dyn_cast<DeclRefExpr>(e)) { + VarDecl *var = dyn_cast<VarDecl>(ref->getDecl()); + if (!var) return false; + return considerVariable(var, ref, owner); + } + + if (BlockDeclRefExpr *ref = dyn_cast<BlockDeclRefExpr>(e)) { + owner.Variable = ref->getDecl(); + owner.setLocsFrom(ref); + return true; + } + + if (MemberExpr *member = dyn_cast<MemberExpr>(e)) { + if (member->isArrow()) return false; + + // Don't count this as an indirect ownership. + e = member->getBase(); + continue; + } + + // Array ivars? + + return false; + } +} + +namespace { + struct FindCaptureVisitor : EvaluatedExprVisitor<FindCaptureVisitor> { + FindCaptureVisitor(ASTContext &Context, VarDecl *variable) + : EvaluatedExprVisitor<FindCaptureVisitor>(Context), + Variable(variable), Capturer(0) {} + + VarDecl *Variable; + Expr *Capturer; + + void VisitDeclRefExpr(DeclRefExpr *ref) { + if (ref->getDecl() == Variable && !Capturer) + Capturer = ref; + } + + void VisitBlockDeclRefExpr(BlockDeclRefExpr *ref) { + if (ref->getDecl() == Variable && !Capturer) + Capturer = ref; + } + + void VisitObjCIvarRefExpr(ObjCIvarRefExpr *ref) { + if (Capturer) return; + Visit(ref->getBase()); + if (Capturer && ref->isFreeIvar()) + Capturer = ref; + } + + void VisitBlockExpr(BlockExpr *block) { + // Look inside nested blocks + if (block->getBlockDecl()->capturesVariable(Variable)) + Visit(block->getBlockDecl()->getBody()); + } + }; +} + +/// Check whether the given argument is a block which captures a +/// variable. +static Expr *findCapturingExpr(Sema &S, Expr *e, RetainCycleOwner &owner) { + assert(owner.Variable && owner.Loc.isValid()); + + e = e->IgnoreParenCasts(); + BlockExpr *block = dyn_cast<BlockExpr>(e); + if (!block || !block->getBlockDecl()->capturesVariable(owner.Variable)) + return 0; + + FindCaptureVisitor visitor(S.Context, owner.Variable); + visitor.Visit(block->getBlockDecl()->getBody()); + return visitor.Capturer; +} + +static void diagnoseRetainCycle(Sema &S, Expr *capturer, + RetainCycleOwner &owner) { + assert(capturer); + assert(owner.Variable && owner.Loc.isValid()); + + S.Diag(capturer->getExprLoc(), diag::warn_arc_retain_cycle) + << owner.Variable << capturer->getSourceRange(); + S.Diag(owner.Loc, diag::note_arc_retain_cycle_owner) + << owner.Indirect << owner.Range; +} + +/// Check for a keyword selector that starts with the word 'add' or +/// 'set'. +static bool isSetterLikeSelector(Selector sel) { + if (sel.isUnarySelector()) return false; + + llvm::StringRef str = sel.getNameForSlot(0); + while (!str.empty() && str.front() == '_') str = str.substr(1); + if (str.startswith("set") || str.startswith("add")) + str = str.substr(3); + else + return false; + + if (str.empty()) return true; + return !islower(str.front()); +} + +/// Check a message send to see if it's likely to cause a retain cycle. +void Sema::checkRetainCycles(ObjCMessageExpr *msg) { + // Only check instance methods whose selector looks like a setter. + if (!msg->isInstanceMessage() || !isSetterLikeSelector(msg->getSelector())) + return; + + // Try to find a variable that the receiver is strongly owned by. + RetainCycleOwner owner; + if (msg->getReceiverKind() == ObjCMessageExpr::Instance) { + if (!findRetainCycleOwner(msg->getInstanceReceiver(), owner)) + return; + } else { + assert(msg->getReceiverKind() == ObjCMessageExpr::SuperInstance); + owner.Variable = getCurMethodDecl()->getSelfDecl(); + owner.Loc = msg->getSuperLoc(); + owner.Range = msg->getSuperLoc(); + } + + // Check whether the receiver is captured by any of the arguments. + for (unsigned i = 0, e = msg->getNumArgs(); i != e; ++i) + if (Expr *capturer = findCapturingExpr(*this, msg->getArg(i), owner)) + return diagnoseRetainCycle(*this, capturer, owner); +} + +/// Check a property assign to see if it's likely to cause a retain cycle. +void Sema::checkRetainCycles(Expr *receiver, Expr *argument) { + RetainCycleOwner owner; + if (!findRetainCycleOwner(receiver, owner)) + return; + + if (Expr *capturer = findCapturingExpr(*this, argument, owner)) + diagnoseRetainCycle(*this, capturer, owner); +} + +void Sema::checkUnsafeAssigns(SourceLocation Loc, + QualType LHS, Expr *RHS) { + Qualifiers::ObjCLifetime LT = LHS.getObjCLifetime(); + if (LT != Qualifiers::OCL_Weak && LT != Qualifiers::OCL_ExplicitNone) + return; + if (ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(RHS)) + if (cast->getCastKind() == CK_ObjCConsumeObject) + Diag(Loc, diag::warn_arc_retained_assign) + << (LT == Qualifiers::OCL_ExplicitNone) + << RHS->getSourceRange(); +} + diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index e328eeb0aa2..69b38593fbf 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -1676,6 +1676,34 @@ static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC, // Fall through: conditions and statements can have expressions. case Sema::PCC_ParenthesizedExpression: + if (SemaRef.getLangOptions().ObjCAutoRefCount && + CCC == Sema::PCC_ParenthesizedExpression) { + // (__bridge <type>)<expression> + Builder.AddTypedTextChunk("__bridge"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("type"); + Builder.AddChunk(CodeCompletionString::CK_RightParen); + Builder.AddPlaceholderChunk("expression"); + Results.AddResult(Result(Builder.TakeString())); + + // (__bridge_transfer <Objective-C type>)<expression> + Builder.AddTypedTextChunk("__bridge_transfer"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("Objective-C type"); + Builder.AddChunk(CodeCompletionString::CK_RightParen); + Builder.AddPlaceholderChunk("expression"); + Results.AddResult(Result(Builder.TakeString())); + + // (__bridge_retained <CF type>)<expression> + Builder.AddTypedTextChunk("__bridge_retained"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("CF type"); + Builder.AddChunk(CodeCompletionString::CK_RightParen); + Builder.AddPlaceholderChunk("expression"); + Results.AddResult(Result(Builder.TakeString())); + } + // Fall through + case Sema::PCC_Expression: { if (SemaRef.getLangOptions().CPlusPlus) { // 'this', if we're in a non-static member function. @@ -1828,7 +1856,8 @@ static const char *GetCompletionTypeString(QualType T, CodeCompletionAllocator &Allocator) { PrintingPolicy Policy(Context.PrintingPolicy); Policy.AnonymousTagLocations = false; - + Policy.SuppressStrongLifetime = true; + if (!T.getLocalQualifiers()) { // Built-in type names are constant strings. if (const BuiltinType *BT = dyn_cast<BuiltinType>(T)) @@ -1878,9 +1907,9 @@ static void AddResultTypeChunk(ASTContext &Context, T = Context.getTypeDeclType(cast<TypeDecl>(Enumerator->getDeclContext())); else if (isa<UnresolvedUsingValueDecl>(ND)) { /* Do nothing: ignore unresolved using declarations*/ - } else if (ValueDecl *Value = dyn_cast<ValueDecl>(ND)) + } else if (ValueDecl *Value = dyn_cast<ValueDecl>(ND)) { T = Value->getType(); - else if (ObjCPropertyDecl *Property = dyn_cast<ObjCPropertyDecl>(ND)) + } else if (ObjCPropertyDecl *Property = dyn_cast<ObjCPropertyDecl>(ND)) T = Property->getType(); if (T.isNull() || Context.hasSameType(T, Context.DependentTy)) @@ -1907,6 +1936,10 @@ static void MaybeAddSentinel(ASTContext &Context, NamedDecl *FunctionOrMethod, static std::string FormatFunctionParameter(ASTContext &Context, ParmVarDecl *Param, bool SuppressName = false) { + PrintingPolicy Policy(Context.PrintingPolicy); + Policy.AnonymousTagLocations = false; + Policy.SuppressStrongLifetime = true; + bool ObjCMethodParam = isa<ObjCMethodDecl>(Param->getDeclContext()); if (Param->getType()->isDependentType() || !Param->getType()->isBlockPointerType()) { @@ -1917,8 +1950,7 @@ static std::string FormatFunctionParameter(ASTContext &Context, if (Param->getIdentifier() && !ObjCMethodParam && !SuppressName) Result = Param->getIdentifier()->getName(); - Param->getType().getAsStringInternal(Result, - Context.PrintingPolicy); + Param->getType().getAsStringInternal(Result, Policy); if (ObjCMethodParam) { Result = "(" + Result; @@ -1968,8 +2000,7 @@ static std::string FormatFunctionParameter(ASTContext &Context, // We were unable to find a FunctionProtoTypeLoc with parameter names // for the block; just use the parameter type as a placeholder. std::string Result; - Param->getType().getUnqualifiedType(). - getAsStringInternal(Result, Context.PrintingPolicy); + Param->getType().getUnqualifiedType().getAsStringInternal(Result, Policy); if (ObjCMethodParam) { Result = "(" + Result; @@ -1986,7 +2017,7 @@ static std::string FormatFunctionParameter(ASTContext &Context, std::string Result; QualType ResultType = Block->getTypePtr()->getResultType(); if (!ResultType->isVoidType()) - ResultType.getAsStringInternal(Result, Context.PrintingPolicy); + ResultType.getAsStringInternal(Result, Policy); Result = '^' + Result; if (!BlockProto || Block->getNumArgs() == 0) { @@ -2071,6 +2102,9 @@ static void AddTemplateParameterChunks(ASTContext &Context, unsigned MaxParameters = 0, unsigned Start = 0, bool InDefaultArg = false) { + PrintingPolicy Policy(Context.PrintingPolicy); + Policy.AnonymousTagLocations = false; + typedef CodeCompletionString::Chunk Chunk; bool FirstParameter = true; @@ -2098,8 +2132,7 @@ static void AddTemplateParameterChunks(ASTContext &Context, = dyn_cast<NonTypeTemplateParmDecl>(*P)) { if (NTTP->getIdentifier()) PlaceholderStr = NTTP->getIdentifier()->getName(); - NTTP->getType().getAsStringInternal(PlaceholderStr, - Context.PrintingPolicy); + NTTP->getType().getAsStringInternal(PlaceholderStr, Policy); HasDefaultArg = NTTP->hasDefaultArgument(); } else { assert(isa<TemplateTemplateParmDecl>(*P)); @@ -2286,6 +2319,10 @@ CodeCompletionResult::CreateCodeCompletionString(Sema &S, typedef CodeCompletionString::Chunk Chunk; CodeCompletionBuilder Result(Allocator, Priority, Availability); + PrintingPolicy Policy(S.Context.PrintingPolicy); + Policy.AnonymousTagLocations = false; + Policy.SuppressStrongLifetime = true; + if (Kind == RK_Pattern) { Pattern->Priority = Priority; Pattern->Availability = Availability; @@ -2470,7 +2507,7 @@ CodeCompletionResult::CreateCodeCompletionString(Sema &S, if ((*P)->getType()->isBlockPointerType() && !DeclaringEntity) Arg = FormatFunctionParameter(S.Context, *P, true); else { - (*P)->getType().getAsStringInternal(Arg, S.Context.PrintingPolicy); + (*P)->getType().getAsStringInternal(Arg, Policy); Arg = "(" + Arg + ")"; if (IdentifierInfo *II = (*P)->getIdentifier()) if (DeclaringEntity || AllParametersAreInformative) @@ -2519,7 +2556,10 @@ CodeCompleteConsumer::OverloadCandidate::CreateSignatureString( Sema &S, CodeCompletionAllocator &Allocator) const { typedef CodeCompletionString::Chunk Chunk; - + PrintingPolicy Policy(S.Context.PrintingPolicy); + Policy.AnonymousTagLocations = false; + Policy.SuppressStrongLifetime = true; + // FIXME: Set priority, availability appropriately. CodeCompletionBuilder Result(Allocator, 1, CXAvailability_Available); FunctionDecl *FDecl = getFunction(); @@ -2545,7 +2585,7 @@ CodeCompleteConsumer::OverloadCandidate::CreateSignatureString( else Result.AddTextChunk( Result.getAllocator().CopyString( - Proto->getResultType().getAsString(S.Context.PrintingPolicy))); + Proto->getResultType().getAsString(Policy))); Result.AddChunk(Chunk(CodeCompletionString::CK_LeftParen)); unsigned NumParams = FDecl? FDecl->getNumParams() : Proto->getNumArgs(); @@ -2563,7 +2603,7 @@ CodeCompleteConsumer::OverloadCandidate::CreateSignatureString( ArgType = Proto->getArgType(I); } - ArgType.getAsStringInternal(ArgString, S.Context.PrintingPolicy); + ArgType.getAsStringInternal(ArgString, Policy); if (I == CurrentArg) Result.AddChunk(Chunk(CodeCompletionString::CK_CurrentParameter, @@ -3786,6 +3826,10 @@ void Sema::CodeCompleteOperatorName(Scope *S) { void Sema::CodeCompleteConstructorInitializer(Decl *ConstructorD, CXXCtorInitializer** Initializers, unsigned NumInitializers) { + PrintingPolicy Policy(Context.PrintingPolicy); + Policy.AnonymousTagLocations = false; + Policy.SuppressStrongLifetime = true; + CXXConstructorDecl *Constructor = static_cast<CXXConstructorDecl *>(ConstructorD); if (!Constructor) @@ -3825,7 +3869,7 @@ void Sema::CodeCompleteConstructorInitializer(Decl *ConstructorD, Builder.AddTypedTextChunk( Results.getAllocator().CopyString( - Base->getType().getAsString(Context.PrintingPolicy))); + Base->getType().getAsString(Policy))); Builder.AddChunk(CodeCompletionString::CK_LeftParen); Builder.AddPlaceholderChunk("args"); Builder.AddChunk(CodeCompletionString::CK_RightParen); @@ -3850,7 +3894,7 @@ void Sema::CodeCompleteConstructorInitializer(Decl *ConstructorD, Builder.AddTypedTextChunk( Builder.getAllocator().CopyString( - Base->getType().getAsString(Context.PrintingPolicy))); + Base->getType().getAsString(Policy))); Builder.AddChunk(CodeCompletionString::CK_LeftParen); Builder.AddPlaceholderChunk("args"); Builder.AddChunk(CodeCompletionString::CK_RightParen); @@ -4126,18 +4170,24 @@ static bool ObjCPropertyFlagConflicts(unsigned Attributes, unsigned NewFlag) { if ((Attributes & ObjCDeclSpec::DQ_PR_readonly) && (Attributes & (ObjCDeclSpec::DQ_PR_readwrite | ObjCDeclSpec::DQ_PR_assign | + ObjCDeclSpec::DQ_PR_unsafe_unretained | ObjCDeclSpec::DQ_PR_copy | - ObjCDeclSpec::DQ_PR_retain))) + ObjCDeclSpec::DQ_PR_retain | + ObjCDeclSpec::DQ_PR_strong))) return true; - // Check for more than one of { assign, copy, retain }. + // Check for more than one of { assign, copy, retain, strong }. unsigned AssignCopyRetMask = Attributes & (ObjCDeclSpec::DQ_PR_assign | + ObjCDeclSpec::DQ_PR_unsafe_unretained | ObjCDeclSpec::DQ_PR_copy | - ObjCDeclSpec::DQ_PR_retain); + ObjCDeclSpec::DQ_PR_retain| + ObjCDeclSpec::DQ_PR_strong); if (AssignCopyRetMask && AssignCopyRetMask != ObjCDeclSpec::DQ_PR_assign && + AssignCopyRetMask != ObjCDeclSpec::DQ_PR_unsafe_unretained && AssignCopyRetMask != ObjCDeclSpec::DQ_PR_copy && - AssignCopyRetMask != ObjCDeclSpec::DQ_PR_retain) + AssignCopyRetMask != ObjCDeclSpec::DQ_PR_retain && + AssignCopyRetMask != ObjCDeclSpec::DQ_PR_strong) return true; return false; @@ -4157,10 +4207,15 @@ void Sema::CodeCompleteObjCPropertyFlags(Scope *S, ObjCDeclSpec &ODS) { Results.AddResult(CodeCompletionResult("readonly")); if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_assign)) Results.AddResult(CodeCompletionResult("assign")); + if (!ObjCPropertyFlagConflicts(Attributes, + ObjCDeclSpec::DQ_PR_unsafe_unretained)) + Results.AddResult(CodeCompletionResult("unsafe_unretained")); if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_readwrite)) Results.AddResult(CodeCompletionResult("readwrite")); if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_retain)) Results.AddResult(CodeCompletionResult("retain")); + if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_strong)) + Results.AddResult(CodeCompletionResult("strong")); if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_copy)) Results.AddResult(CodeCompletionResult("copy")); if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_nonatomic)) @@ -4522,6 +4577,7 @@ static ObjCInterfaceDecl *GetAssumedMessageSendExprType(Expr *E) { if (Method->isInstanceMethod()) return llvm::StringSwitch<ObjCInterfaceDecl *>(Id->getName()) .Case("retain", IFace) + .Case("strong", IFace) .Case("autorelease", IFace) .Case("copy", IFace) .Case("copyWithZone", IFace) @@ -6273,6 +6329,7 @@ void Sema::CodeCompleteObjCMethodDecl(Scope *S, Results.EnterNewScope(); PrintingPolicy Policy(Context.PrintingPolicy); Policy.AnonymousTagLocations = false; + Policy.SuppressStrongLifetime = true; for (KnownMethodsMap::iterator M = KnownMethods.begin(), MEnd = KnownMethods.end(); M != MEnd; ++M) { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index c7545cf5324..4476211bc0d 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -1928,6 +1928,7 @@ bool Sema::MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old) { return false; } + void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod, const ObjCMethodDecl *oldMethod) { // Merge the attributes. @@ -3473,6 +3474,50 @@ static void SetNestedNameSpecifier(DeclaratorDecl *DD, Declarator &D) { DD->setQualifierInfo(SS.getWithLocInContext(DD->getASTContext())); } +bool Sema::inferObjCARCLifetime(ValueDecl *decl) { + QualType type = decl->getType(); + Qualifiers::ObjCLifetime lifetime = type.getObjCLifetime(); + if (lifetime == Qualifiers::OCL_Autoreleasing) { + // Various kinds of declaration aren't allowed to be __autoreleasing. + unsigned kind = -1U; + if (VarDecl *var = dyn_cast<VarDecl>(decl)) { + if (var->hasAttr<BlocksAttr>()) + kind = 0; // __block + else if (!var->hasLocalStorage()) + kind = 1; // global + } else if (isa<ObjCIvarDecl>(decl)) { + kind = 3; // ivar + } else if (isa<FieldDecl>(decl)) { + kind = 2; // field + } + + if (kind != -1U) { + Diag(decl->getLocation(), diag::err_arc_autoreleasing_var) + << kind; + } + } else if (lifetime == Qualifiers::OCL_None) { + // Try to infer lifetime. + if (!type->isObjCLifetimeType()) + return false; + + lifetime = type->getObjCARCImplicitLifetime(); + type = Context.getLifetimeQualifiedType(type, lifetime); + decl->setType(type); + } + + if (VarDecl *var = dyn_cast<VarDecl>(decl)) { + // Thread-local variables cannot have lifetime. + if (lifetime && lifetime != Qualifiers::OCL_ExplicitNone && + var->isThreadSpecified()) { + Diag(var->getLocation(), diag::err_arc_thread_lifetime) + << var->getType(); + return true; + } + } + + return false; +} + NamedDecl* Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, QualType R, TypeSourceInfo *TInfo, @@ -3631,6 +3676,11 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, // Handle attributes prior to checking for duplicates in MergeVarDecl ProcessDeclAttributes(S, NewVD, D); + // In auto-retain/release, infer strong retension for variables of + // retainable type. + if (getLangOptions().ObjCAutoRefCount && inferObjCARCLifetime(NewVD)) + NewVD->setInvalidDecl(); + // Handle GNU asm-label extension (encoded as an attribute). if (Expr *E = (Expr*)D.getAsmLabel()) { // The parser guarantees this is a string. @@ -5229,6 +5279,10 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, VDecl->setTypeSourceInfo(DeducedType); VDecl->setType(DeducedType->getType()); + // In ARC, infer lifetime. + if (getLangOptions().ObjCAutoRefCount && inferObjCARCLifetime(VDecl)) + VDecl->setInvalidDecl(); + // If this is a redeclaration, check that the type we just deduced matches // the previously declared type. if (VarDecl *Old = VDecl->getPreviousDeclaration()) @@ -5471,7 +5525,10 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, // Check any implicit conversions within the expression. CheckImplicitConversions(Init, VDecl->getLocation()); - + + if (!VDecl->isInvalidDecl()) + checkUnsafeAssigns(VDecl->getLocation(), VDecl->getType(), Init); + Init = MaybeCreateExprWithCleanups(Init); // Attach the initializer to the decl. VDecl->setInit(Init); @@ -5740,6 +5797,23 @@ void Sema::ActOnCXXForRangeDecl(Decl *D) { void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { if (var->isInvalidDecl()) return; + // In ARC, don't allow jumps past the implicit initialization of a + // local retaining variable. + if (getLangOptions().ObjCAutoRefCount && + var->hasLocalStorage()) { + switch (var->getType().getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + break; + + case Qualifiers::OCL_Weak: + case Qualifiers::OCL_Strong: + getCurFunction()->setHasBranchProtectedScope(); + break; + } + } + // All the following checks are C++ only. if (!getLangOptions().CPlusPlus) return; @@ -6008,7 +6082,7 @@ void Sema::DiagnoseSizeOfParametersAndReturnValue(ParmVarDecl * const *Param, // Warn if the return value is pass-by-value and larger than the specified // threshold. - if (ReturnTy->isPODType()) { + if (ReturnTy.isPODType(Context)) { unsigned Size = Context.getTypeSizeInChars(ReturnTy).getQuantity(); if (Size > LangOpts.NumLargeByValueCopy) Diag(D->getLocation(), diag::warn_return_value_size) @@ -6019,7 +6093,7 @@ void Sema::DiagnoseSizeOfParametersAndReturnValue(ParmVarDecl * const *Param, // threshold. for (; Param != ParamEnd; ++Param) { QualType T = (*Param)->getType(); - if (!T->isPODType()) + if (!T.isPODType(Context)) continue; unsigned Size = Context.getTypeSizeInChars(T).getQuantity(); if (Size > LangOpts.NumLargeByValueCopy) @@ -6033,6 +6107,28 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc, QualType T, TypeSourceInfo *TSInfo, VarDecl::StorageClass StorageClass, VarDecl::StorageClass StorageClassAsWritten) { + // In ARC, infer a lifetime qualifier for appropriate parameter types. + if (getLangOptions().ObjCAutoRefCount && + T.getObjCLifetime() == Qualifiers::OCL_None && + T->isObjCLifetimeType()) { + + Qualifiers::ObjCLifetime lifetime; + + // Special cases for arrays: + // - if it's const, use __unsafe_unretained + // - otherwise, it's an error + if (T->isArrayType()) { + if (!T.isConstQualified()) { + Diag(NameLoc, diag::err_arc_array_param_no_lifetime) + << TSInfo->getTypeLoc().getSourceRange(); + } + lifetime = Qualifiers::OCL_ExplicitNone; + } else { + lifetime = T->getObjCARCImplicitLifetime(); + } + T = Context.getLifetimeQualifiedType(T, lifetime); + } + ParmVarDecl *New = ParmVarDecl::Create(Context, DC, StartLoc, NameLoc, Name, adjustParameterType(T), TSInfo, StorageClass, StorageClassAsWritten, @@ -6369,7 +6465,7 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, // Verify that that gotos and switch cases don't jump into scopes illegally. if (getCurFunction()->NeedsScopeChecking() && !dcl->isInvalidDecl() && - !hasAnyErrorsInThisFunction()) + !hasAnyUnrecoverableErrorsInThisFunction()) DiagnoseInvalidJumps(Body); if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(dcl)) { @@ -6384,15 +6480,17 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, // been leftover. This ensures that these temporaries won't be picked up for // deletion in some later function. if (PP.getDiagnostics().hasErrorOccurred() || - PP.getDiagnostics().getSuppressAllDiagnostics()) + PP.getDiagnostics().getSuppressAllDiagnostics()) { ExprTemporaries.clear(); - else if (!isa<FunctionTemplateDecl>(dcl)) { + ExprNeedsCleanups = false; + } else if (!isa<FunctionTemplateDecl>(dcl)) { // Since the body is valid, issue any analysis-based warnings that are // enabled. ActivePolicy = &WP; } assert(ExprTemporaries.empty() && "Leftover temporaries in function"); + assert(!ExprNeedsCleanups && "Unaccounted cleanups in function"); } if (!IsInstantiation) @@ -6403,8 +6501,10 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, // If any errors have occurred, clear out any temporaries that may have // been leftover. This ensures that these temporaries won't be picked up for // deletion in some later function. - if (getDiagnostics().hasErrorOccurred()) + if (getDiagnostics().hasErrorOccurred()) { ExprTemporaries.clear(); + ExprNeedsCleanups = false; + } return dcl; } @@ -7736,6 +7836,11 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, // FIXME: What to pass instead of TUScope? ProcessDeclAttributes(TUScope, NewFD, *D); + // In auto-retain/release, infer strong retension for fields of + // retainable type. + if (getLangOptions().ObjCAutoRefCount && inferObjCARCLifetime(NewFD)) + NewFD->setInvalidDecl(); + if (T.isObjCGCWeak()) Diag(Loc, diag::warn_attribute_weak_on_field); @@ -7769,6 +7874,21 @@ bool Sema::CheckNontrivialField(FieldDecl *FD) { member = CXXDestructor; if (member != CXXInvalid) { + if (getLangOptions().ObjCAutoRefCount && RDecl->hasObjectMember()) { + // Objective-C++ ARC: it is an error to have a non-trivial field of + // a union. However, system headers in Objective-C programs + // occasionally have Objective-C lifetime objects within unions, + // and rather than cause the program to fail, we make those + // members unavailable. + SourceLocation Loc = FD->getLocation(); + if (getSourceManager().isInSystemHeader(Loc)) { + if (!FD->hasAttr<UnavailableAttr>()) + FD->addAttr(new (Context) UnavailableAttr(Loc, Context, + "this system field has retaining lifetime")); + return false; + } + } + Diag(FD->getLocation(), diag::err_illegal_union_or_anon_struct_member) << (int)FD->getParent()->isUnion() << FD->getDeclName() << member; DiagnoseNontrivial(RT, member); @@ -7921,6 +8041,21 @@ void Sema::DiagnoseNontrivial(const RecordType* T, CXXSpecialMember member) { return; } } + + if (EltTy->isObjCLifetimeType()) { + switch (EltTy.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + break; + + case Qualifiers::OCL_Autoreleasing: + case Qualifiers::OCL_Weak: + case Qualifiers::OCL_Strong: + Diag((*fi)->getLocation(), diag::note_nontrivial_objc_lifetime) + << QT << EltTy.getObjCLifetime(); + return; + } + } } assert(0 && "found no explanation for non-trivial member"); @@ -8030,6 +8165,10 @@ Decl *Sema::ActOnIvar(Scope *S, if (D.isInvalidType()) NewID->setInvalidDecl(); + // In ARC, infer 'retaining' for ivars of retainable type. + if (getLangOptions().ObjCAutoRefCount && inferObjCARCLifetime(NewID)) + NewID->setInvalidDecl(); + if (II) { // FIXME: When interfaces are DeclContexts, we'll need to add // these to the interface. @@ -8101,6 +8240,7 @@ void Sema::ActOnFields(Scope* S, llvm::SmallVector<FieldDecl*, 32> RecFields; RecordDecl *Record = dyn_cast<RecordDecl>(EnclosingDecl); + bool ARCErrReported = false; for (unsigned i = 0; i != NumFields; ++i) { FieldDecl *FD = cast<FieldDecl>(Fields[i]); @@ -8166,7 +8306,7 @@ void Sema::ActOnFields(Scope* S, continue; } if (!FD->getType()->isDependentType() && - !Context.getBaseElementType(FD->getType())->isPODType()) { + !Context.getBaseElementType(FD->getType()).isPODType(Context)) { Diag(FD->getLocation(), diag::err_flexible_array_has_nonpod_type) << FD->getDeclName() << FD->getType(); FD->setInvalidDecl(); @@ -8213,17 +8353,45 @@ void Sema::ActOnFields(Scope* S, FD->setInvalidDecl(); EnclosingDecl->setInvalidDecl(); continue; - } else if (getLangOptions().ObjC1 && + } + else if (!getLangOptions().CPlusPlus) { + if (getLangOptions().ObjCAutoRefCount && Record && !ARCErrReported) { + // It's an error in ARC if a field has lifetime. + // We don't want to report this in a system header, though, + // so we just make the field unavailable. + // FIXME: that's really not sufficient; we need to make the type + // itself invalid to, say, initialize or copy. + QualType T = FD->getType(); + Qualifiers::ObjCLifetime lifetime = T.getObjCLifetime(); + if (lifetime && lifetime != Qualifiers::OCL_ExplicitNone) { + SourceLocation loc = FD->getLocation(); + if (getSourceManager().isInSystemHeader(loc)) { + if (!FD->hasAttr<UnavailableAttr>()) { + FD->addAttr(new (Context) UnavailableAttr(loc, Context, + "this system field has retaining lifetime")); + } + } else { + Diag(FD->getLocation(), diag::err_arc_objc_object_in_struct); + } + ARCErrReported = true; + } + } + else if (getLangOptions().ObjC1 && getLangOptions().getGCMode() != LangOptions::NonGC && - Record && - (FD->getType()->isObjCObjectPointerType() || - FD->getType().isObjCGCStrong())) - Record->setHasObjectMember(true); - else if (Context.getAsArrayType(FD->getType())) { - QualType BaseType = Context.getBaseElementType(FD->getType()); - if (Record && BaseType->isRecordType() && - BaseType->getAs<RecordType>()->getDecl()->hasObjectMember()) - Record->setHasObjectMember(true); + Record && !Record->hasObjectMember()) { + if (FD->getType()->isObjCObjectPointerType() || + FD->getType().isObjCGCStrong()) + Record->setHasObjectMember(true); + else if (Context.getAsArrayType(FD->getType())) { + QualType BaseType = Context.getBaseElementType(FD->getType()); + if (BaseType->isRecordType() && + BaseType->getAs<RecordType>()->getDecl()->hasObjectMember()) + Record->setHasObjectMember(true); + else if (BaseType->isObjCObjectPointerType() || + BaseType.isObjCGCStrong()) + Record->setHasObjectMember(true); + } + } } // Keep track of the number of named members. if (FD->getIdentifier()) @@ -8242,6 +8410,42 @@ void Sema::ActOnFields(Scope* S, Convs->setAccess(I, (*I)->getAccess()); if (!CXXRecord->isDependentType()) { + // 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. + // + // This rule is also handled by CXXRecordDecl::completeDefinition(). + // However, here we check whether this particular class is only + // non-POD because of the presence of an Objective-C pointer member. + // If so, objects of this type cannot be shared between code compiled + // with instant objects and code compiled with manual retain/release. + if (getLangOptions().ObjCAutoRefCount && + CXXRecord->hasObjectMember() && + CXXRecord->getLinkage() == ExternalLinkage) { + if (CXXRecord->isPOD()) { + Diag(CXXRecord->getLocation(), + diag::warn_arc_non_pod_class_with_object_member) + << CXXRecord; + } else { + // FIXME: Fix-Its would be nice here, but finding a good location + // for them is going to be tricky. + if (CXXRecord->hasTrivialCopyConstructor()) + Diag(CXXRecord->getLocation(), + diag::warn_arc_trivial_member_function_with_object_member) + << CXXRecord << 0; + if (CXXRecord->hasTrivialCopyAssignment()) + Diag(CXXRecord->getLocation(), + diag::warn_arc_trivial_member_function_with_object_member) + << CXXRecord << 1; + if (CXXRecord->hasTrivialDestructor()) + Diag(CXXRecord->getLocation(), + diag::warn_arc_trivial_member_function_with_object_member) + << CXXRecord << 2; + } + } + // Adjust user-defined destructor exception spec. if (getLangOptions().CPlusPlus0x && CXXRecord->hasUserDeclaredDestructor()) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 430b4f99fb2..4769b092642 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" +#include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/DelayedDiagnostic.h" @@ -101,8 +102,9 @@ static bool isFunctionOrMethodOrBlock(const Decl *d) { /// Return true if the given decl has a declarator that should have /// been processed by Sema::GetTypeForDeclarator. static bool hasDeclarator(const Decl *d) { - // In some sense, TypedefNameDecl really *ought* to be a DeclaratorDecl. - return isa<DeclaratorDecl>(d) || isa<BlockDecl>(d) || isa<TypedefNameDecl>(d); + // In some sense, TypedefDecl really *ought* to be a DeclaratorDecl. + return isa<DeclaratorDecl>(d) || isa<BlockDecl>(d) || isa<TypedefNameDecl>(d) || + isa<ObjCPropertyDecl>(d); } /// hasFunctionProto - Return true if the given decl has a argument @@ -1216,8 +1218,16 @@ static void HandleObjCMethodFamilyAttr(Decl *decl, const AttributeList &attr, return; } - decl->addAttr(new (S.Context) ObjCMethodFamilyAttr(attr.getLoc(), - S.Context, family)); + if (family == ObjCMethodFamilyAttr::OMF_init && + !method->getResultType()->isObjCObjectPointerType()) { + S.Diag(method->getLocation(), diag::err_init_method_bad_return_type) + << method->getResultType(); + // Ignore the attribute. + return; + } + + method->addAttr(new (S.Context) ObjCMethodFamilyAttr(attr.getLoc(), + S.Context, family)); } static void HandleObjCExceptionAttr(Decl *D, const AttributeList &Attr, @@ -2706,6 +2716,9 @@ static void HandleNSReturnsRetainedAttr(Decl *d, const AttributeList &attr, if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(d)) returnType = MD->getResultType(); + else if (S.getLangOptions().ObjCAutoRefCount && hasDeclarator(d) && + (attr.getKind() == AttributeList::AT_ns_returns_retained)) + return; // ignore: was handled as a type attribute else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(d)) returnType = FD->getResultType(); else { @@ -2767,6 +2780,62 @@ static void HandleNSReturnsRetainedAttr(Decl *d, const AttributeList &attr, }; } +static void HandleObjCLifetimeAttr(Decl *d, const AttributeList &attr, + Sema &S) { + if (hasDeclarator(d)) return; + + SourceLocation L = attr.getLoc(); + S.Diag(d->getLocStart(), diag::err_attribute_wrong_decl_type) + << SourceRange(L, L) << attr.getName() << 12 /* variable */; +} + +static void HandleObjCPreciseLifetimeAttr(Decl *d, const AttributeList &attr, + Sema &S) { + if (!isa<VarDecl>(d) && !isa<FieldDecl>(d)) { + SourceLocation L = attr.getLoc(); + S.Diag(d->getLocStart(), diag::err_attribute_wrong_decl_type) + << SourceRange(L, L) << attr.getName() << 12 /* variable */; + return; + } + + ValueDecl *vd = cast<ValueDecl>(d); + QualType type = vd->getType(); + + if (!type->isDependentType() && + !type->isObjCLifetimeType()) { + S.Diag(attr.getLoc(), diag::err_objc_precise_lifetime_bad_type) + << type; + return; + } + + Qualifiers::ObjCLifetime lifetime = type.getObjCLifetime(); + + // If we have no lifetime yet, check the lifetime we're presumably + // going to infer. + if (lifetime == Qualifiers::OCL_None && !type->isDependentType()) + lifetime = type->getObjCARCImplicitLifetime(); + + switch (lifetime) { + case Qualifiers::OCL_None: + assert(type->isDependentType() && + "didn't infer lifetime for non-dependent type?"); + break; + + case Qualifiers::OCL_Weak: // meaningful + case Qualifiers::OCL_Strong: // meaningful + break; + + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + S.Diag(attr.getLoc(), diag::warn_objc_precise_lifetime_meaningless) + << (lifetime == Qualifiers::OCL_Autoreleasing); + break; + } + + d->addAttr(::new (S.Context) + ObjCPreciseLifetimeAttr(attr.getLoc(), S.Context)); +} + static bool isKnownDeclSpecAttr(const AttributeList &Attr) { return Attr.getKind() == AttributeList::AT_dllimport || Attr.getKind() == AttributeList::AT_dllexport || @@ -2909,6 +2978,11 @@ static void ProcessInheritableDeclAttr(Scope *scope, Decl *D, case AttributeList::AT_shared: HandleSharedAttr (D, Attr, S); break; case AttributeList::AT_vecreturn: HandleVecReturnAttr (D, Attr, S); break; + case AttributeList::AT_objc_lifetime: + HandleObjCLifetimeAttr(D, Attr, S); break; + case AttributeList::AT_objc_precise_lifetime: + HandleObjCPreciseLifetimeAttr(D, Attr, S); break; + // Checker-specific. case AttributeList::AT_cf_consumed: case AttributeList::AT_ns_consumed: HandleNSConsumedAttr (D, Attr, S); break; @@ -3117,6 +3191,32 @@ void Sema::ProcessDeclAttributes(Scope *S, Decl *D, const Declarator &PD, ProcessDeclAttributeList(S, D, Attrs, NonInheritable, Inheritable); } +/// Is the given declaration allowed to use a forbidden type? +static bool isForbiddenTypeAllowed(Sema &S, Decl *decl) { + // Private ivars are always okay. Unfortunately, people don't + // always properly make their ivars private, even in system headers. + // Plus we need to make fields okay, too. + if (!isa<FieldDecl>(decl) && !isa<ObjCPropertyDecl>(decl)) + return false; + + // Require it to be declared in a system header. + return S.Context.getSourceManager().isInSystemHeader(decl->getLocation()); +} + +/// Handle a delayed forbidden-type diagnostic. +static void handleDelayedForbiddenType(Sema &S, DelayedDiagnostic &diag, + Decl *decl) { + if (decl && isForbiddenTypeAllowed(S, decl)) { + decl->addAttr(new (S.Context) UnavailableAttr(diag.Loc, S.Context, + "this system declaration uses an unsupported type")); + return; + } + + S.Diag(diag.Loc, diag.getForbiddenTypeDiagnostic()) + << diag.getForbiddenTypeOperand() << diag.getForbiddenTypeArgument(); + diag.Triggered = true; +} + // This duplicates a vector push_back but hides the need to know the // size of the type. void Sema::DelayedDiagnostics::add(const DelayedDiagnostic &diag) { @@ -3179,6 +3279,10 @@ void Sema::DelayedDiagnostics::popParsingDecl(Sema &S, ParsingDeclState state, case DelayedDiagnostic::Access: S.HandleDelayedAccessCheck(diag, decl); break; + + case DelayedDiagnostic::ForbiddenType: + handleDelayedForbiddenType(S, diag, decl); + break; } } } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index ce99efbd0bd..09597d2ef1f 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -762,13 +762,6 @@ bool Sema::AttachBaseSpecifiers(CXXRecordDecl *Class, CXXBaseSpecifier **Bases, QualType NewBaseType = Context.getCanonicalType(Bases[idx]->getType()); NewBaseType = NewBaseType.getLocalUnqualifiedType(); - if (!Class->hasObjectMember()) { - if (const RecordType *FDTTy = - NewBaseType.getTypePtr()->getAs<RecordType>()) - if (FDTTy->getDecl()->hasObjectMember()) - Class->setHasObjectMember(true); - } - if (KnownBaseTypes[NewBaseType]) { // C++ [class.mi]p3: // A class shall not be specified as a direct base class of a @@ -1619,12 +1612,7 @@ Sema::BuildMemberInitializer(ValueDecl *Member, Expr **Args, Init = new (Context) ParenListExpr(Context, LParenLoc, Args, NumArgs, RParenLoc); - // Erase any temporaries within this evaluation context; we're not - // going to track them in the AST, since we'll be rebuilding the - // ASTs during template instantiation. - ExprTemporaries.erase( - ExprTemporaries.begin() + ExprEvalContexts.back().NumTemporaries, - ExprTemporaries.end()); + DiscardCleanupsInEvaluationContext(); } else { // Initialize the member. InitializedEntity MemberEntity = @@ -1817,12 +1805,7 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo, = Owned(new (Context) ParenListExpr(Context, LParenLoc, Args, NumArgs, RParenLoc)); - // Erase any temporaries within this evaluation context; we're not - // going to track them in the AST, since we'll be rebuilding the - // ASTs during template instantiation. - ExprTemporaries.erase( - ExprTemporaries.begin() + ExprEvalContexts.back().NumTemporaries, - ExprTemporaries.end()); + DiscardCleanupsInEvaluationContext(); return new (Context) CXXCtorInitializer(Context, BaseTInfo, /*IsVirtual=*/false, @@ -2134,6 +2117,20 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, } } + if (SemaRef.getLangOptions().ObjCAutoRefCount && + FieldBaseElementType->isObjCRetainableType() && + FieldBaseElementType.getObjCLifetime() != Qualifiers::OCL_None && + FieldBaseElementType.getObjCLifetime() != Qualifiers::OCL_ExplicitNone) { + // Instant objects: + // Default-initialize Objective-C pointers to NULL. + CXXMemberInit + = new (SemaRef.Context) CXXCtorInitializer(SemaRef.Context, Field, + Loc, Loc, + new (SemaRef.Context) ImplicitValueInitExpr(Field->getType()), + Loc); + return false; + } + // Nothing to initialize. CXXMemberInit = 0; return false; @@ -7041,10 +7038,8 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation, // explicit assignments, do so. This optimization only applies for arrays // of scalars and arrays of class type with trivial copy-assignment // operators. - if (FieldType->isArrayType() && - (!BaseType->isRecordType() || - cast<CXXRecordDecl>(BaseType->getAs<RecordType>()->getDecl()) - ->hasTrivialCopyAssignment())) { + if (FieldType->isArrayType() && + BaseType.hasTrivialCopyAssignment(Context)) { // Compute the size of the memory buffer to be copied. QualType SizeType = Context.getSizeType(); llvm::APInt Size(Context.getTypeSize(SizeType), @@ -7523,6 +7518,10 @@ void Sema::AddCXXDirectInitializerToDecl(Decl *RealDecl, VDecl->setTypeSourceInfo(DeducedType); VDecl->setType(DeducedType->getType()); + // In ARC, infer lifetime. + if (getLangOptions().ObjCAutoRefCount && inferObjCARCLifetime(VDecl)) + VDecl->setInvalidDecl(); + // If this is a redeclaration, check that the type we just deduced matches // the previously declared type. if (VarDecl *Old = VDecl->getPreviousDeclaration()) diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index bfa2ef45d08..563de6fd15f 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -16,14 +16,96 @@ #include "clang/Sema/ExternalSemaSource.h" #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" +#include "clang/AST/ASTConsumer.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" +#include "clang/Basic/SourceManager.h" #include "clang/Sema/DeclSpec.h" #include "llvm/ADT/DenseSet.h" using namespace clang; +/// Check whether the given method, which must be in the 'init' +/// family, is a valid member of that family. +/// +/// \param receiverTypeIfCall - if null, check this as if declaring it; +/// if non-null, check this as if making a call to it with the given +/// receiver type +/// +/// \return true to indicate that there was an error and appropriate +/// actions were taken +bool Sema::checkInitMethod(ObjCMethodDecl *method, + QualType receiverTypeIfCall) { + if (method->isInvalidDecl()) return true; + + // This castAs is safe: methods that don't return an object + // pointer won't be inferred as inits and will reject an explicit + // objc_method_family(init). + + // We ignore protocols here. Should we? What about Class? + + const ObjCObjectType *result = method->getResultType() + ->castAs<ObjCObjectPointerType>()->getObjectType(); + + if (result->isObjCId()) { + return false; + } else if (result->isObjCClass()) { + // fall through: always an error + } else { + ObjCInterfaceDecl *resultClass = result->getInterface(); + assert(resultClass && "unexpected object type!"); + + // It's okay for the result type to still be a forward declaration + // if we're checking an interface declaration. + if (resultClass->isForwardDecl()) { + if (receiverTypeIfCall.isNull() && + !isa<ObjCImplementationDecl>(method->getDeclContext())) + return false; + + // Otherwise, we try to compare class types. + } else { + // If this method was declared in a protocol, we can't check + // anything unless we have a receiver type that's an interface. + const ObjCInterfaceDecl *receiverClass = 0; + if (isa<ObjCProtocolDecl>(method->getDeclContext())) { + if (receiverTypeIfCall.isNull()) + return false; + + receiverClass = receiverTypeIfCall->castAs<ObjCObjectPointerType>() + ->getInterfaceDecl(); + + // This can be null for calls to e.g. id<Foo>. + if (!receiverClass) return false; + } else { + receiverClass = method->getClassInterface(); + assert(receiverClass && "method not associated with a class!"); + } + + // If either class is a subclass of the other, it's fine. + if (receiverClass->isSuperClassOf(resultClass) || + resultClass->isSuperClassOf(receiverClass)) + return false; + } + } + + SourceLocation loc = method->getLocation(); + + // If we're in a system header, and this is not a call, just make + // the method unusable. + if (receiverTypeIfCall.isNull() && getSourceManager().isInSystemHeader(loc)) { + method->addAttr(new (Context) UnavailableAttr(loc, Context, + "init method returns a type unrelated to its receiver type")); + return true; + } + + // Otherwise, it's an error. + Diag(loc, diag::err_arc_init_method_unrelated_result_type); + method->setInvalidDecl(); + return true; +} + bool Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod, const ObjCMethodDecl *Overridden, bool IsImplementation) { @@ -36,7 +118,7 @@ bool Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod, QualType ResultType = NewMethod->getResultType(); SourceRange ResultTypeRange; if (const TypeSourceInfo *ResultTypeInfo - = NewMethod->getResultTypeSourceInfo()) + = NewMethod->getResultTypeSourceInfo()) ResultTypeRange = ResultTypeInfo->getTypeLoc().getSourceRange(); // Figure out which class this method is part of, if any. @@ -107,10 +189,10 @@ static bool CheckObjCMethodOverrides(Sema &S, ObjCMethodDecl *NewMethod, if (CheckObjCMethodOverrides(S, NewMethod, Category, false)) return true; } - + // Look through protocols. for (ObjCList<ObjCProtocolDecl>::iterator I = Class->protocol_begin(), - IEnd = Class->protocol_end(); + IEnd = Class->protocol_end(); I != IEnd; ++I) if (CheckObjCMethodOverrides(S, NewMethod, *I, false)) return true; @@ -123,7 +205,7 @@ static bool CheckObjCMethodOverrides(Sema &S, ObjCMethodDecl *NewMethod, if (ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(DC)) { // Look through protocols. for (ObjCList<ObjCProtocolDecl>::iterator I = Category->protocol_begin(), - IEnd = Category->protocol_end(); + IEnd = Category->protocol_end(); I != IEnd; ++I) if (CheckObjCMethodOverrides(S, NewMethod, *I, false)) return true; @@ -134,7 +216,7 @@ static bool CheckObjCMethodOverrides(Sema &S, ObjCMethodDecl *NewMethod, if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(DC)) { // Look through protocols. for (ObjCList<ObjCProtocolDecl>::iterator I = Protocol->protocol_begin(), - IEnd = Protocol->protocol_end(); + IEnd = Protocol->protocol_end(); I != IEnd; ++I) if (CheckObjCMethodOverrides(S, NewMethod, *I, false)) return true; @@ -149,13 +231,13 @@ bool Sema::CheckObjCMethodOverrides(ObjCMethodDecl *NewMethod, DeclContext *DC) { if (ObjCInterfaceDecl *Class = dyn_cast<ObjCInterfaceDecl>(DC)) return ::CheckObjCMethodOverrides(*this, NewMethod, Class); - + if (ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(DC)) return ::CheckObjCMethodOverrides(*this, NewMethod, Category); - + if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(DC)) return ::CheckObjCMethodOverrides(*this, NewMethod, Protocol); - + if (ObjCImplementationDecl *Impl = dyn_cast<ObjCImplementationDecl>(DC)) return ::CheckObjCMethodOverrides(*this, NewMethod, Impl->getClassInterface()); @@ -167,6 +249,50 @@ bool Sema::CheckObjCMethodOverrides(ObjCMethodDecl *NewMethod, return ::CheckObjCMethodOverrides(*this, NewMethod, CurContext); } +/// \brief Check a method declaration for compatibility with the Objective-C +/// ARC conventions. +static bool CheckARCMethodDecl(Sema &S, ObjCMethodDecl *method) { + ObjCMethodFamily family = method->getMethodFamily(); + switch (family) { + case OMF_None: + case OMF_dealloc: + case OMF_retain: + case OMF_release: + case OMF_autorelease: + case OMF_retainCount: + case OMF_self: + return false; + + case OMF_init: + // If the method doesn't obey the init rules, don't bother annotating it. + if (S.checkInitMethod(method, QualType())) + return true; + + method->addAttr(new (S.Context) NSConsumesSelfAttr(SourceLocation(), + S.Context)); + + // Don't add a second copy of this attribute, but otherwise don't + // let it be suppressed. + if (method->hasAttr<NSReturnsRetainedAttr>()) + return false; + break; + + case OMF_alloc: + case OMF_copy: + case OMF_mutableCopy: + case OMF_new: + if (method->hasAttr<NSReturnsRetainedAttr>() || + method->hasAttr<NSReturnsNotRetainedAttr>() || + method->hasAttr<NSReturnsAutoreleasedAttr>()) + return false; + break; + } + + method->addAttr(new (S.Context) NSReturnsRetainedAttr(SourceLocation(), + S.Context)); + return false; +} + static void DiagnoseObjCImplementedDeprecations(Sema &S, NamedDecl *ND, SourceLocation ImplLoc, @@ -220,6 +346,30 @@ void Sema::ActOnStartOfObjCMethodDef(Scope *FnBodyScope, Decl *D) { if ((*PI)->getIdentifier()) PushOnScopeChains(*PI, FnBodyScope); } + + // In ARC, disallow definition of retain/release/autorelease/retainCount + if (getLangOptions().ObjCAutoRefCount) { + switch (MDecl->getMethodFamily()) { + case OMF_retain: + case OMF_retainCount: + case OMF_release: + case OMF_autorelease: + Diag(MDecl->getLocation(), diag::err_arc_illegal_method_def) + << MDecl->getSelector(); + break; + + case OMF_None: + case OMF_dealloc: + case OMF_alloc: + case OMF_init: + case OMF_mutableCopy: + case OMF_copy: + case OMF_new: + case OMF_self: + break; + } + } + // Warn on implementating deprecated methods under // -Wdeprecated-implementations flag. if (ObjCInterfaceDecl *IC = MDecl->getClassInterface()) @@ -1099,11 +1249,83 @@ static void CheckMethodOverrideParam(Sema &S, S.Diag(IfaceVar->getLocation(), diag::note_previous_definition) << getTypeRange(IfaceVar->getTypeSourceInfo()); } - + +/// In ARC, check whether the conventional meanings of the two methods +/// match. If they don't, it's a hard error. +static bool checkMethodFamilyMismatch(Sema &S, ObjCMethodDecl *impl, + ObjCMethodDecl *decl) { + ObjCMethodFamily implFamily = impl->getMethodFamily(); + ObjCMethodFamily declFamily = decl->getMethodFamily(); + if (implFamily == declFamily) return false; + + // Since conventions are sorted by selector, the only possibility is + // that the types differ enough to cause one selector or the other + // to fall out of the family. + assert(implFamily == OMF_None || declFamily == OMF_None); + + // No further diagnostics required on invalid declarations. + if (impl->isInvalidDecl() || decl->isInvalidDecl()) return true; + + const ObjCMethodDecl *unmatched = impl; + ObjCMethodFamily family = declFamily; + unsigned errorID = diag::err_arc_lost_method_convention; + unsigned noteID = diag::note_arc_lost_method_convention; + if (declFamily == OMF_None) { + unmatched = decl; + family = implFamily; + errorID = diag::err_arc_gained_method_convention; + noteID = diag::note_arc_gained_method_convention; + } + + // Indexes into a %select clause in the diagnostic. + enum FamilySelector { + F_alloc, F_copy, F_mutableCopy = F_copy, F_init, F_new + }; + FamilySelector familySelector = FamilySelector(); + + switch (family) { + case OMF_None: llvm_unreachable("logic error, no method convention"); + case OMF_retain: + case OMF_release: + case OMF_autorelease: + case OMF_dealloc: + case OMF_retainCount: + case OMF_self: + // Mismatches for these methods don't change ownership + // conventions, so we don't care. + return false; + + case OMF_init: familySelector = F_init; break; + case OMF_alloc: familySelector = F_alloc; break; + case OMF_copy: familySelector = F_copy; break; + case OMF_mutableCopy: familySelector = F_mutableCopy; break; + case OMF_new: familySelector = F_new; break; + } + + enum ReasonSelector { R_NonObjectReturn, R_UnrelatedReturn }; + ReasonSelector reasonSelector; + + // The only reason these methods don't fall within their families is + // due to unusual result types. + if (unmatched->getResultType()->isObjCObjectPointerType()) { + reasonSelector = R_UnrelatedReturn; + } else { + reasonSelector = R_NonObjectReturn; + } + + S.Diag(impl->getLocation(), errorID) << familySelector << reasonSelector; + S.Diag(decl->getLocation(), noteID) << familySelector << reasonSelector; + + return true; +} void Sema::WarnConflictingTypedMethods(ObjCMethodDecl *ImpMethodDecl, ObjCMethodDecl *MethodDecl, bool IsProtocolMethodDecl) { + if (getLangOptions().ObjCAutoRefCount && + checkMethodFamilyMismatch(*this, ImpMethodDecl, MethodDecl)) + return; + CheckMethodOverrideReturn(*this, ImpMethodDecl, MethodDecl, IsProtocolMethodDecl); @@ -1430,48 +1652,82 @@ Sema::ActOnForwardClassDeclaration(SourceLocation AtClassLoc, return CDecl; } +static bool matchTypes(ASTContext &Context, Sema::MethodMatchStrategy strategy, + QualType leftQT, QualType rightQT) { + const Type *left = + Context.getCanonicalType(leftQT).getUnqualifiedType().getTypePtr(); + const Type *right = + Context.getCanonicalType(rightQT).getUnqualifiedType().getTypePtr(); + + if (left == right) return true; + + // If we're doing a strict match, the types have to match exactly. + if (strategy == Sema::MMS_strict) return false; + + if (left->isIncompleteType() || right->isIncompleteType()) return false; + + // Otherwise, use this absurdly complicated algorithm to try to + // validate the basic, low-level compatibility of the two types. + + // As a minimum, require the sizes and alignments to match. + if (Context.getTypeInfo(left) != Context.getTypeInfo(right)) + return false; + + // Consider all the kinds of non-dependent canonical types: + // - functions and arrays aren't possible as return and parameter types + + // - vector types of equal size can be arbitrarily mixed + if (isa<VectorType>(left)) return isa<VectorType>(right); + if (isa<VectorType>(right)) return false; + + // - references should only match references of identical type + // - structs, unions, and Objective-C objects must match exactly + // - everything else should be a scalar + if (!left->isScalarType() || !right->isScalarType()) + return false; + + // Make scalars agree in kind, except count bools as chars. + Type::ScalarTypeKind leftSK = left->getScalarTypeKind(); + Type::ScalarTypeKind rightSK = right->getScalarTypeKind(); + if (leftSK == Type::STK_Bool) leftSK = Type::STK_Integral; + if (rightSK == Type::STK_Bool) rightSK = Type::STK_Integral; + + // Note that data member pointers and function member pointers don't + // intermix because of the size differences. + + return (leftSK == rightSK); +} /// MatchTwoMethodDeclarations - Checks that two methods have matching type and /// returns true, or false, accordingly. /// TODO: Handle protocol list; such as id<p1,p2> in type comparisons -bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *Method, - const ObjCMethodDecl *PrevMethod, - bool matchBasedOnSizeAndAlignment, - bool matchBasedOnStrictEqulity) { - QualType T1 = Context.getCanonicalType(Method->getResultType()); - QualType T2 = Context.getCanonicalType(PrevMethod->getResultType()); - - if (T1 != T2) { - // The result types are different. - if (!matchBasedOnSizeAndAlignment || matchBasedOnStrictEqulity) - return false; - // Incomplete types don't have a size and alignment. - if (T1->isIncompleteType() || T2->isIncompleteType()) - return false; - // Check is based on size and alignment. - if (Context.getTypeInfo(T1) != Context.getTypeInfo(T2)) - return false; - } +bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *left, + const ObjCMethodDecl *right, + MethodMatchStrategy strategy) { + if (!matchTypes(Context, strategy, + left->getResultType(), right->getResultType())) + return false; - ObjCMethodDecl::param_iterator ParamI = Method->param_begin(), - E = Method->param_end(); - ObjCMethodDecl::param_iterator PrevI = PrevMethod->param_begin(); + if (getLangOptions().ObjCAutoRefCount && + (left->hasAttr<NSReturnsRetainedAttr>() + != right->hasAttr<NSReturnsRetainedAttr>() || + left->hasAttr<NSConsumesSelfAttr>() + != right->hasAttr<NSConsumesSelfAttr>())) + return false; - for (; ParamI != E; ++ParamI, ++PrevI) { - assert(PrevI != PrevMethod->param_end() && "Param mismatch"); - T1 = Context.getCanonicalType((*ParamI)->getType()); - T2 = Context.getCanonicalType((*PrevI)->getType()); - if (T1 != T2) { - // The result types are different. - if (!matchBasedOnSizeAndAlignment || matchBasedOnStrictEqulity) - return false; - // Incomplete types don't have a size and alignment. - if (T1->isIncompleteType() || T2->isIncompleteType()) - return false; - // Check is based on size and alignment. - if (Context.getTypeInfo(T1) != Context.getTypeInfo(T2)) - return false; - } + ObjCMethodDecl::param_iterator + li = left->param_begin(), le = left->param_end(), ri = right->param_begin(); + + for (; li != le; ++li, ++ri) { + assert(ri != right->param_end() && "Param mismatch"); + ParmVarDecl *lparm = *li, *rparm = *ri; + + if (!matchTypes(Context, strategy, lparm->getType(), rparm->getType())) + return false; + + if (getLangOptions().ObjCAutoRefCount && + lparm->hasAttr<NSConsumedAttr>() != rparm->hasAttr<NSConsumedAttr>()) + return false; } return true; } @@ -1513,8 +1769,10 @@ void Sema::AddMethodToGlobalPool(ObjCMethodDecl *Method, bool impl, // We've seen a method with this name, see if we have already seen this type // signature. - for (ObjCMethodList *List = &Entry; List; List = List->Next) - if (MatchTwoMethodDeclarations(Method, List->Method)) { + for (ObjCMethodList *List = &Entry; List; List = List->Next) { + bool match = MatchTwoMethodDeclarations(Method, List->Method); + + if (match) { ObjCMethodDecl *PrevObjCMethod = List->Method; PrevObjCMethod->setDefined(impl); // If a method is deprecated, push it in the global pool. @@ -1531,6 +1789,7 @@ void Sema::AddMethodToGlobalPool(ObjCMethodDecl *Method, bool impl, } return; } + } // We have a new signature for an existing method - add it. // This is extremely rare. Only 1% of Cocoa selectors are "overloaded". @@ -1538,6 +1797,25 @@ void Sema::AddMethodToGlobalPool(ObjCMethodDecl *Method, bool impl, Entry.Next = new (Mem) ObjCMethodList(Method, Entry.Next); } +/// Determines if this is an "acceptable" loose mismatch in the global +/// method pool. This exists mostly as a hack to get around certain +/// global mismatches which we can't afford to make warnings / errors. +/// Really, what we want is a way to take a method out of the global +/// method pool. +static bool isAcceptableMethodMismatch(ObjCMethodDecl *chosen, + ObjCMethodDecl *other) { + if (!chosen->isInstanceMethod()) + return false; + + Selector sel = chosen->getSelector(); + if (!sel.isUnarySelector() || sel.getNameForSlot(0) != "length") + return false; + + // Don't complain about mismatches for -length if the method we + // chose has an integral result type. + return (chosen->getResultType()->isIntegerType()); +} + ObjCMethodDecl *Sema::LookupMethodInGlobalPool(Selector Sel, SourceRange R, bool receiverIdOrClass, bool warn, bool instance) { @@ -1551,32 +1829,52 @@ ObjCMethodDecl *Sema::LookupMethodInGlobalPool(Selector Sel, SourceRange R, ObjCMethodList &MethList = instance ? Pos->second.first : Pos->second.second; - bool strictSelectorMatch = receiverIdOrClass && warn && - (Diags.getDiagnosticLevel(diag::warn_strict_multiple_method_decl, - R.getBegin()) != - Diagnostic::Ignored); if (warn && MethList.Method && MethList.Next) { - bool issueWarning = false; + bool issueDiagnostic = false, issueError = false; + + // We support a warning which complains about *any* difference in + // method signature. + bool strictSelectorMatch = + (receiverIdOrClass && warn && + (Diags.getDiagnosticLevel(diag::warn_strict_multiple_method_decl, + R.getBegin()) != + Diagnostic::Ignored)); if (strictSelectorMatch) for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next) { - // This checks if the methods differ in type mismatch. - if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method, false, true)) - issueWarning = true; + if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method, + MMS_strict)) { + issueDiagnostic = true; + break; + } } - if (!issueWarning) + // If we didn't see any strict differences, we won't see any loose + // differences. In ARC, however, we also need to check for loose + // mismatches, because most of them are errors. + if (!strictSelectorMatch || + (issueDiagnostic && getLangOptions().ObjCAutoRefCount)) for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next) { - // This checks if the methods differ by size & alignment. - if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method, true)) - issueWarning = true; + // This checks if the methods differ in type mismatch. + if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method, + MMS_loose) && + !isAcceptableMethodMismatch(MethList.Method, Next->Method)) { + issueDiagnostic = true; + if (getLangOptions().ObjCAutoRefCount) + issueError = true; + break; + } } - if (issueWarning) { - if (strictSelectorMatch) + if (issueDiagnostic) { + if (issueError) + Diag(R.getBegin(), diag::err_arc_multiple_method_decl) << Sel << R; + else if (strictSelectorMatch) Diag(R.getBegin(), diag::warn_strict_multiple_method_decl) << Sel << R; else Diag(R.getBegin(), diag::warn_multiple_method_decl) << Sel << R; - Diag(MethList.Method->getLocStart(), diag::note_using) + + Diag(MethList.Method->getLocStart(), + issueError ? diag::note_possibility : diag::note_using) << MethList.Method->getSourceRange(); for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next) Diag(Next->Method->getLocStart(), diag::note_also_found) @@ -1804,6 +2102,7 @@ void Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, DefaultSynthesizeProperties(S, IC, IDecl); ImplMethodsVsClassMethods(S, IC, IDecl); AtomicPropertySetterGetterRules(IC, IDecl); + DiagnoseOwningPropertyGetterSynthesis(IC); if (LangOpts.ObjCNonFragileABI2) while (IDecl->getSuperClass()) { @@ -2112,7 +2411,11 @@ Decl *Sema::ActOnMethodDeclaration( mergeObjCMethodDecls(ObjCMethod, InterfaceMD); } - if (!ObjCMethod->hasRelatedResultType() && + bool ARCError = false; + if (getLangOptions().ObjCAutoRefCount) + ARCError = CheckARCMethodDecl(*this, ObjCMethod); + + if (!ObjCMethod->hasRelatedResultType() && !ARCError && getLangOptions().ObjCInferRelatedResultType) { bool InferRelatedResultType = false; switch (ObjCMethod->getMethodFamily()) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index a2ad16145af..f0ecb5800c0 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -87,7 +87,7 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { if (FD->isDeleted()) { Diag(Loc, diag::err_deleted_function_use); - Diag(D->getLocation(), diag::note_unavailable_here) << true; + Diag(D->getLocation(), diag::note_unavailable_here) << 1 << true; return true; } } @@ -114,7 +114,8 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, else Diag(Loc, diag::err_unavailable_message) << D->getDeclName() << Message; - Diag(D->getLocation(), diag::note_unavailable_here) << 0; + Diag(D->getLocation(), diag::note_unavailable_here) + << isa<FunctionDecl>(D) << false; break; } @@ -437,7 +438,7 @@ ExprResult Sema::DefaultArgumentPromotion(Expr *E) { /// will warn if the resulting type is not a POD type, and rejects ObjC /// interfaces passed by value. ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT, - FunctionDecl *FDecl) { + FunctionDecl *FDecl) { ExprResult ExprRes = DefaultArgumentPromotion(E); if (ExprRes.isInvalid()) return ExprError(); @@ -456,7 +457,7 @@ ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT, << E->getType() << CT)) return ExprError(); - if (!E->getType()->isPODType()) { + if (!E->getType().isPODType(Context)) { // C++0x [expr.call]p7: // Passing a potentially-evaluated argument of class type (Clause 9) // having a non-trivial copy constructor, a non-trivial move constructor, @@ -471,6 +472,11 @@ ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT, TrivialEnough = true; } } + + if (!TrivialEnough && + getLangOptions().ObjCAutoRefCount && + E->getType()->isObjCLifetimeType()) + TrivialEnough = true; if (TrivialEnough) { // Nothing to diagnose. This is okay. @@ -1545,9 +1551,11 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef, // An id-expression that denotes a non-static data member or non-static // member function of a class can only be used: // (...) - // - if that id-expression denotes a non-static data member and it appears in an unevaluated operand. - const Sema::ExpressionEvaluationContextRecord& record = SemaRef.ExprEvalContexts.back(); - bool isUnevaluatedExpression = record.Context == Sema::Unevaluated; + // - if that id-expression denotes a non-static data member and it + // appears in an unevaluated operand. + const Sema::ExpressionEvaluationContextRecord& record + = SemaRef.ExprEvalContexts.back(); + bool isUnevaluatedExpression = (record.Context == Sema::Unevaluated); if (isUnevaluatedExpression) return IMA_Mixed_StaticContext; } @@ -4241,6 +4249,9 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, // - an interface ObjCInterfaceDecl *IDecl = OTy->getInterface(); if (!IDecl) { + if (getLangOptions().ObjCAutoRefCount && + (OTy->isObjCId() || OTy->isObjCClass())) + goto fail; // There's an implicit 'isa' ivar on all objects. // But we only actually find it this way on objects of type 'id', // apparently. @@ -4730,6 +4741,7 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, MarkDeclarationReferenced(Param->getDefaultArg()->getLocStart(), const_cast<CXXDestructorDecl*>(Temporary->getDestructor())); ExprTemporaries.push_back(Temporary); + ExprNeedsCleanups = true; } // We already type-checked the argument, so we know it works. @@ -4848,7 +4860,8 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, InitializedEntity Entity = Param? InitializedEntity::InitializeParameter(Context, Param) - : InitializedEntity::InitializeParameter(Context, ProtoArgType); + : InitializedEntity::InitializeParameter(Context, ProtoArgType, + Proto->isArgConsumed(i)); ExprResult ArgE = PerformCopyInitialization(Entity, SourceLocation(), Owned(Arg)); @@ -5171,7 +5184,8 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, if (Proto && i < Proto->getNumArgs()) { InitializedEntity Entity = InitializedEntity::InitializeParameter(Context, - Proto->getArgType(i)); + Proto->getArgType(i), + Proto->isArgConsumed(i)); ExprResult ArgE = PerformCopyInitialization(Entity, SourceLocation(), Owned(Arg)); @@ -5262,8 +5276,8 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo, InitializedEntity Entity = InitializedEntity::InitializeTemporary(literalType); InitializationKind Kind - = InitializationKind::CreateCast(SourceRange(LParenLoc, RParenLoc), - /*IsCStyleCast=*/true); + = InitializationKind::CreateCStyleCast(LParenLoc, + SourceRange(LParenLoc, RParenLoc)); InitializationSequence InitSeq(*this, Entity, Kind, &literalExpr, 1); ExprResult Result = InitSeq.Perform(*this, Entity, Kind, MultiExprArg(*this, &literalExpr, 1), @@ -5440,14 +5454,15 @@ static CastKind PrepareScalarCast(Sema &S, ExprResult &Src, QualType DestTy) { } /// CheckCastTypes - Check type constraints for casting between types. -ExprResult Sema::CheckCastTypes(SourceRange TyR, QualType castType, - Expr *castExpr, CastKind& Kind, ExprValueKind &VK, +ExprResult Sema::CheckCastTypes(SourceLocation CastStartLoc, SourceRange TyR, + QualType castType, Expr *castExpr, + CastKind& Kind, ExprValueKind &VK, CXXCastPath &BasePath, bool FunctionalStyle) { if (castExpr->getType() == Context.UnknownAnyTy) return checkUnknownAnyCast(TyR, castType, castExpr, Kind, VK, BasePath); if (getLangOptions().CPlusPlus) - return CXXCheckCStyleCast(SourceRange(TyR.getBegin(), + return CXXCheckCStyleCast(SourceRange(CastStartLoc, castExpr->getLocEnd()), castType, VK, castExpr, Kind, BasePath, FunctionalStyle); @@ -5565,8 +5580,8 @@ ExprResult Sema::CheckCastTypes(SourceRange TyR, QualType castType, // If either type is a pointer, the other type has to be either an // integer or a pointer. + QualType castExprType = castExpr->getType(); if (!castType->isArithmeticType()) { - QualType castExprType = castExpr->getType(); if (!castExprType->isIntegralType(Context) && castExprType->isArithmeticType()) { Diag(castExpr->getLocStart(), @@ -5582,6 +5597,29 @@ ExprResult Sema::CheckCastTypes(SourceRange TyR, QualType castType, } } + if (getLangOptions().ObjCAutoRefCount) { + // Diagnose problems with Objective-C casts involving lifetime qualifiers. + CheckObjCARCConversion(SourceRange(CastStartLoc, castExpr->getLocEnd()), + castType, castExpr, CCK_CStyleCast); + + if (const PointerType *CastPtr = castType->getAs<PointerType>()) { + if (const PointerType *ExprPtr = castExprType->getAs<PointerType>()) { + Qualifiers CastQuals = CastPtr->getPointeeType().getQualifiers(); + Qualifiers ExprQuals = ExprPtr->getPointeeType().getQualifiers(); + if (CastPtr->getPointeeType()->isObjCLifetimeType() && + ExprPtr->getPointeeType()->isObjCLifetimeType() && + !CastQuals.compatiblyIncludesObjCLifetime(ExprQuals)) { + Diag(castExpr->getLocStart(), + diag::err_typecheck_incompatible_lifetime) + << castExprType << castType << AA_Casting + << castExpr->getSourceRange(); + + return ExprError(); + } + } + } + } + castExprRes = Owned(castExpr); Kind = PrepareScalarCast(*this, castExprRes, castType); if (castExprRes.isInvalid()) @@ -5677,8 +5715,8 @@ Sema::BuildCStyleCastExpr(SourceLocation LParenLoc, TypeSourceInfo *Ty, ExprValueKind VK = VK_RValue; CXXCastPath BasePath; ExprResult CastResult = - CheckCastTypes(SourceRange(LParenLoc, RParenLoc), Ty->getType(), castExpr, - Kind, VK, BasePath); + CheckCastTypes(LParenLoc, SourceRange(LParenLoc, RParenLoc), Ty->getType(), + castExpr, Kind, VK, BasePath); if (CastResult.isInvalid()) return ExprError(); castExpr = CastResult.take(); @@ -6441,17 +6479,31 @@ checkPointerTypesForAssignment(Sema &S, QualType lhsType, QualType rhsType) { // qualifiers of the type *pointed to* by the right; Qualifiers lq; + // As a special case, 'non-__weak A *' -> 'non-__weak const *' is okay. + if (lhq.getObjCLifetime() != rhq.getObjCLifetime() && + lhq.compatiblyIncludesObjCLifetime(rhq)) { + // Ignore lifetime for further calculation. + lhq.removeObjCLifetime(); + rhq.removeObjCLifetime(); + } + if (!lhq.compatiblyIncludes(rhq)) { // Treat address-space mismatches as fatal. TODO: address subspaces if (lhq.getAddressSpace() != rhq.getAddressSpace()) ConvTy = Sema::IncompatiblePointerDiscardsQualifiers; - // It's okay to add or remove GC qualifiers when converting to + // It's okay to add or remove GC or lifetime qualifiers when converting to // and from void*. - else if (lhq.withoutObjCGCAttr().compatiblyIncludes(rhq.withoutObjCGCAttr()) + else if (lhq.withoutObjCGCAttr().withoutObjCGLifetime() + .compatiblyIncludes( + rhq.withoutObjCGCAttr().withoutObjCGLifetime()) && (lhptee->isVoidType() || rhptee->isVoidType())) ; // keep old + // Treat lifetime mismatches as fatal. + else if (lhq.getObjCLifetime() != rhq.getObjCLifetime()) + ConvTy = Sema::IncompatiblePointerDiscardsQualifiers; + // For GCC compatibility, other qualifier mismatches are treated // as still compatible in C. else ConvTy = Sema::CompatiblePointerDiscardsQualifiers; @@ -8118,7 +8170,40 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { unsigned Diag = 0; bool NeedType = false; switch (IsLV) { // C99 6.5.16p2 - case Expr::MLV_ConstQualified: Diag = diag::err_typecheck_assign_const; break; + case Expr::MLV_ConstQualified: + Diag = diag::err_typecheck_assign_const; + + // In ARC, use some specialized diagnostics for the times when we + // infer const. + if (S.getLangOptions().ObjCAutoRefCount) { + DeclRefExpr *declRef = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()); + if (declRef && isa<VarDecl>(declRef->getDecl())) { + VarDecl *var = cast<VarDecl>(declRef->getDecl()); + + // If the variable wasn't written with 'const', there are some + // cases where we infer const anyway: + // - self + // - fast enumeration variables + if (!var->getTypeSourceInfo() || + !var->getTypeSourceInfo()->getType().isConstQualified()) { + ObjCMethodDecl *method = S.getCurMethodDecl(); + if (method && var == method->getSelfDecl()) + Diag = diag::err_typecheck_arr_assign_self; + else if (var->getType().getObjCLifetime() + == Qualifiers::OCL_ExplicitNone) + Diag = diag::err_typecheck_arr_assign_enumeration; + SourceRange Assign; + if (Loc != OrigLoc) + Assign = SourceRange(OrigLoc, OrigLoc); + S.Diag(Loc, Diag) << E->getSourceRange() << Assign; + // We need to preserve the AST regardless, so migration tool + // can do its job. + return false; + } + } + } + + break; case Expr::MLV_ArrayType: Diag = diag::err_typecheck_array_not_modifiable_lvalue; NeedType = true; @@ -8233,6 +8318,13 @@ QualType Sema::CheckAssignmentOperands(Expr *LHS, ExprResult &RHS, << SourceRange(UO->getOperatorLoc(), UO->getOperatorLoc()); } } + + if (ConvTy == Compatible) { + if (LHSType.getObjCLifetime() == Qualifiers::OCL_Strong) + checkRetainCycles(LHS, RHS.get()); + else + checkUnsafeAssigns(Loc, LHSType, RHS.get()); + } } else { // Compound assignment "x += y" ConvTy = CheckAssignmentConstraints(Loc, LHSType, RHSType); @@ -8419,6 +8511,8 @@ void Sema::ConvertPropertyForLValue(ExprResult &LHS, ExprResult &RHS, QualType & LHS.get()->getObjectKind() == OK_ObjCProperty); const ObjCPropertyRefExpr *PropRef = LHS.get()->getObjCProperty(); + bool Consumed = false; + if (PropRef->isImplicitProperty()) { // If using property-dot syntax notation for assignment, and there is a // setter, RHS expression is being passed to the setter argument. So, @@ -8426,6 +8520,8 @@ void Sema::ConvertPropertyForLValue(ExprResult &LHS, ExprResult &RHS, QualType & if (const ObjCMethodDecl *SetterMD = PropRef->getImplicitPropertySetter()) { ObjCMethodDecl::param_iterator P = SetterMD->param_begin(); LHSTy = (*P)->getType(); + Consumed = (getLangOptions().ObjCAutoRefCount && + (*P)->hasAttr<NSConsumedAttr>()); // Otherwise, if the getter returns an l-value, just call that. } else { @@ -8437,14 +8533,26 @@ void Sema::ConvertPropertyForLValue(ExprResult &LHS, ExprResult &RHS, QualType & return; } } + } else if (getLangOptions().ObjCAutoRefCount) { + const ObjCMethodDecl *setter + = PropRef->getExplicitProperty()->getSetterMethodDecl(); + if (setter) { + ObjCMethodDecl::param_iterator P = setter->param_begin(); + LHSTy = (*P)->getType(); + Consumed = (*P)->hasAttr<NSConsumedAttr>(); + } } - if (getLangOptions().CPlusPlus && LHSTy->isRecordType()) { + if ((getLangOptions().CPlusPlus && LHSTy->isRecordType()) || + getLangOptions().ObjCAutoRefCount) { InitializedEntity Entity = - InitializedEntity::InitializeParameter(Context, LHSTy); + InitializedEntity::InitializeParameter(Context, LHSTy, Consumed); ExprResult ArgE = PerformCopyInitialization(Entity, SourceLocation(), RHS); - if (!ArgE.isInvalid()) + if (!ArgE.isInvalid()) { RHS = ArgE; + if (getLangOptions().ObjCAutoRefCount && !PropRef->isSuperReceiver()) + checkRetainCycles(const_cast<Expr*>(PropRef->getBase()), RHS.get()); + } } } @@ -9293,6 +9401,29 @@ ExprResult Sema::ActOnAddrLabel(SourceLocation OpLoc, SourceLocation LabLoc, Context.getPointerType(Context.VoidTy))); } +/// Given the last statement in a statement-expression, check whether +/// the result is a producing expression (like a call to an +/// ns_returns_retained function) and, if so, rebuild it to hoist the +/// release out of the full-expression. Otherwise, return null. +/// Cannot fail. +static Expr *maybeRebuildARCConsumingStmt(Stmt *s) { + // Should always be wrapped with one of these. + ExprWithCleanups *cleanups = dyn_cast<ExprWithCleanups>(s); + if (!cleanups) return 0; + + ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(cleanups->getSubExpr()); + if (!cast || cast->getCastKind() != CK_ObjCConsumeObject) + return 0; + + // Splice out the cast. This shouldn't modify any interesting + // features of the statement. + Expr *producer = cast->getSubExpr(); + assert(producer->getType() == cast->getType()); + assert(producer->getValueKind() == cast->getValueKind()); + cleanups->setSubExpr(producer); + return cleanups; +} + ExprResult Sema::ActOnStmtExpr(SourceLocation LPLoc, Stmt *SubStmt, SourceLocation RPLoc) { // "({..})" @@ -9320,6 +9451,7 @@ Sema::ActOnStmtExpr(SourceLocation LPLoc, Stmt *SubStmt, LastLabelStmt = Label; LastStmt = Label->getSubStmt(); } + if (Expr *LastE = dyn_cast<Expr>(LastStmt)) { // Do function/array conversion on the last expression, but not // lvalue-to-rvalue. However, initialize an unqualified type. @@ -9329,12 +9461,24 @@ Sema::ActOnStmtExpr(SourceLocation LPLoc, Stmt *SubStmt, Ty = LastExpr.get()->getType().getUnqualifiedType(); if (!Ty->isDependentType() && !LastExpr.get()->isTypeDependent()) { - LastExpr = PerformCopyInitialization( + // In ARC, if the final expression ends in a consume, splice + // the consume out and bind it later. In the alternate case + // (when dealing with a retainable type), the result + // initialization will create a produce. In both cases the + // result will be +1, and we'll need to balance that out with + // a bind. + if (Expr *rebuiltLastStmt + = maybeRebuildARCConsumingStmt(LastExpr.get())) { + LastExpr = rebuiltLastStmt; + } else { + LastExpr = PerformCopyInitialization( InitializedEntity::InitializeResult(LPLoc, Ty, false), SourceLocation(), - LastExpr); + LastExpr); + } + if (LastExpr.isInvalid()) return ExprError(); if (LastExpr.get() != 0) { @@ -9776,7 +9920,7 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, // If we don't have a function type, just build one from nothing. } else { FunctionProtoType::ExtProtoInfo EPI; - EPI.ExtInfo = FunctionType::ExtInfo(NoReturn, false, 0, CC_Default); + EPI.ExtInfo = FunctionType::ExtInfo().withNoReturn(NoReturn); BlockTy = Context.getFunctionType(RetTy, 0, 0, EPI); } @@ -9785,7 +9929,8 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, BlockTy = Context.getBlockPointerType(BlockTy); // If needed, diagnose invalid gotos and switches in the block. - if (getCurFunction()->NeedsScopeChecking() && !hasAnyErrorsInThisFunction()) + if (getCurFunction()->NeedsScopeChecking() && + !hasAnyUnrecoverableErrorsInThisFunction()) DiagnoseInvalidJumps(cast<CompoundStmt>(Body)); BSI->TheDecl->setBody(cast<CompoundStmt>(Body)); @@ -9849,7 +9994,7 @@ ExprResult Sema::BuildVAArgExpr(SourceLocation BuiltinLoc, << TInfo->getTypeLoc().getSourceRange())) return ExprError(); - if (!TInfo->getType()->isPODType()) + if (!TInfo->getType().isPODType(Context)) Diag(TInfo->getTypeLoc().getBeginLoc(), diag::warn_second_parameter_to_va_arg_not_pod) << TInfo->getType() @@ -9948,6 +10093,11 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, if (lhq.getAddressSpace() != rhq.getAddressSpace()) { DiagKind = diag::err_typecheck_incompatible_address_space; break; + + + } else if (lhq.getObjCLifetime() != rhq.getObjCLifetime()) { + DiagKind = diag::err_typecheck_incompatible_lifetime; + break; } llvm_unreachable("unknown error case for discarding qualifiers!"); @@ -10062,7 +10212,10 @@ bool Sema::VerifyIntegerConstantExpression(const Expr *E, llvm::APSInt *Result){ void Sema::PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext) { ExprEvalContexts.push_back( - ExpressionEvaluationContextRecord(NewContext, ExprTemporaries.size())); + ExpressionEvaluationContextRecord(NewContext, + ExprTemporaries.size(), + ExprNeedsCleanups)); + ExprNeedsCleanups = false; } void @@ -10097,15 +10250,27 @@ Sema::PopExpressionEvaluationContext() { // temporaries that we may have created as part of the evaluation of // the expression in that context: they aren't relevant because they // will never be constructed. - if (Rec.Context == Unevaluated && - ExprTemporaries.size() > Rec.NumTemporaries) + if (Rec.Context == Unevaluated) { ExprTemporaries.erase(ExprTemporaries.begin() + Rec.NumTemporaries, ExprTemporaries.end()); + ExprNeedsCleanups = Rec.ParentNeedsCleanups; + + // Otherwise, merge the contexts together. + } else { + ExprNeedsCleanups |= Rec.ParentNeedsCleanups; + } // Destroy the popped expression evaluation record. Rec.Destroy(); } +void Sema::DiscardCleanupsInEvaluationContext() { + ExprTemporaries.erase( + ExprTemporaries.begin() + ExprEvalContexts.back().NumTemporaries, + ExprTemporaries.end()); + ExprNeedsCleanups = false; +} + /// \brief Note that the given declaration was referenced in the source code. /// /// This routine should be invoke whenever a given declaration is referenced @@ -10399,7 +10564,7 @@ void Sema::MarkDeclarationsReferencedInExpr(Expr *E) { /// during overload resolution or within sizeof/alignof/typeof/typeid. bool Sema::DiagRuntimeBehavior(SourceLocation Loc, const Stmt *stmt, const PartialDiagnostic &PD) { - switch (ExprEvalContexts.back().Context ) { + switch (ExprEvalContexts.back().Context) { case Unevaluated: // The argument will never be evaluated, so don't complain. break; diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 2f5a890e51d..c796f12f088 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -691,7 +691,8 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, ExprValueKind VK = VK_RValue; CXXCastPath BasePath; ExprResult CastExpr = - CheckCastTypes(TInfo->getTypeLoc().getSourceRange(), Ty, Exprs[0], + CheckCastTypes(TInfo->getTypeLoc().getBeginLoc(), + TInfo->getTypeLoc().getSourceRange(), Ty, Exprs[0], Kind, VK, BasePath, /*FunctionalStyle=*/true); if (CastExpr.isInvalid()) @@ -902,8 +903,16 @@ Sema::BuildCXXNew(SourceLocation StartLoc, bool UseGlobal, if (CheckAllocatedType(AllocType, TypeRange.getBegin(), TypeRange)) return ExprError(); - QualType ResultType = Context.getPointerType(AllocType); + // In ARC, infer 'retaining' for the allocated + if (getLangOptions().ObjCAutoRefCount && + AllocType.getObjCLifetime() == Qualifiers::OCL_None && + AllocType->isObjCLifetimeType()) { + AllocType = Context.getLifetimeQualifiedType(AllocType, + AllocType->getObjCARCImplicitLifetime()); + } + QualType ResultType = Context.getPointerType(AllocType); + // C++ 5.3.4p6: "The expression in a direct-new-declarator shall have integral // or enumeration type with a non-negative value." if (ArraySize && !ArraySize->isTypeDependent()) { @@ -964,6 +973,14 @@ Sema::BuildCXXNew(SourceLocation StartLoc, bool UseGlobal, } } + // ARC: warn about ABI issues. + if (getLangOptions().ObjCAutoRefCount) { + QualType BaseAllocType = Context.getBaseElementType(AllocType); + if (BaseAllocType.hasStrongOrWeakObjCLifetime()) + Diag(StartLoc, diag::warn_err_new_delete_object_array) + << 0 << BaseAllocType; + } + // Note that we do *not* convert the argument in any way. It can // be signed, larger than size_t, whatever. } @@ -1122,6 +1139,15 @@ bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation Loc, else if (unsigned AddressSpace = AllocType.getAddressSpace()) return Diag(Loc, diag::err_address_space_qualified_new) << AllocType.getUnqualifiedType() << AddressSpace; + else if (getLangOptions().ObjCAutoRefCount) { + if (const ArrayType *AT = Context.getAsArrayType(AllocType)) { + QualType BaseAllocType = Context.getBaseElementType(AT); + if (BaseAllocType.getObjCLifetime() == Qualifiers::OCL_None && + BaseAllocType->isObjCLifetimeType()) + return Diag(Loc, diag::err_arc_new_array_without_lifetime) + << BaseAllocType; + } + } return false; } @@ -1774,8 +1800,9 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, // delete-expression; it is not necessary to cast away the constness // (5.2.11) of the pointer expression before it is used as the operand // of the delete-expression. ] - Ex = ImpCastExprToType(Ex.take(), Context.getPointerType(Context.VoidTy), - CK_NoOp); + if (!Context.hasSameType(Ex.get()->getType(), Context.VoidPtrTy)) + Ex = Owned(ImplicitCastExpr::Create(Context, Context.VoidPtrTy, CK_NoOp, + Ex.take(), 0, VK_RValue)); if (Pointee->isArrayType() && !ArrayForm) { Diag(StartLoc, diag::warn_delete_array_type) @@ -1830,6 +1857,14 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, if (!dtor || !dtor->isVirtual()) Diag(StartLoc, diag::warn_delete_non_virtual_dtor) << PointeeElem; } + + } else if (getLangOptions().ObjCAutoRefCount && + PointeeElem->isObjCLifetimeType() && + (PointeeElem.getObjCLifetime() == Qualifiers::OCL_Strong || + PointeeElem.getObjCLifetime() == Qualifiers::OCL_Weak) && + ArrayForm) { + Diag(StartLoc, diag::warn_err_new_delete_object_array) + << 1 << PointeeElem; } if (!OperatorDelete) { @@ -1988,11 +2023,12 @@ static ExprResult BuildCXXCastArgument(Sema &S, ExprResult Sema::PerformImplicitConversion(Expr *From, QualType ToType, const ImplicitConversionSequence &ICS, - AssignmentAction Action, bool CStyle) { + AssignmentAction Action, + CheckedConversionKind CCK) { switch (ICS.getKind()) { case ImplicitConversionSequence::StandardConversion: { ExprResult Res = PerformImplicitConversion(From, ToType, ICS.Standard, - Action, CStyle); + Action, CCK); if (Res.isInvalid()) return ExprError(); From = Res.take(); @@ -2027,7 +2063,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, ExprResult Res = PerformImplicitConversion(From, BeforeToType, ICS.UserDefined.Before, AA_Converting, - CStyle); + CCK); if (Res.isInvalid()) return ExprError(); From = Res.take(); @@ -2047,7 +2083,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, From = CastArg.take(); return PerformImplicitConversion(From, ToType, ICS.UserDefined.After, - AA_Converting, CStyle); + AA_Converting, CCK); } case ImplicitConversionSequence::AmbiguousConversion: @@ -2076,13 +2112,16 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, ExprResult Sema::PerformImplicitConversion(Expr *From, QualType ToType, const StandardConversionSequence& SCS, - AssignmentAction Action, bool CStyle) { + AssignmentAction Action, + CheckedConversionKind CCK) { + bool CStyle = (CCK == CCK_CStyleCast || CCK == CCK_FunctionalCast); + // Overall FIXME: we are recomputing too many types here and doing far too // much extra work. What this means is that we need to keep track of more // information that is computed when we try the implicit conversion initially, // so that we don't need to recompute anything here. QualType FromType = From->getType(); - + if (SCS.CopyConstructor) { // FIXME: When can ToType be a reference type? assert(!ToType->isReferenceType()); @@ -2149,12 +2188,14 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, case ICK_Array_To_Pointer: FromType = Context.getArrayDecayedType(FromType); - From = ImpCastExprToType(From, FromType, CK_ArrayToPointerDecay).take(); + From = ImpCastExprToType(From, FromType, CK_ArrayToPointerDecay, + VK_RValue, /*BasePath=*/0, CCK).take(); break; case ICK_Function_To_Pointer: FromType = Context.getPointerType(FromType); - From = ImpCastExprToType(From, FromType, CK_FunctionToPointerDecay).take(); + From = ImpCastExprToType(From, FromType, CK_FunctionToPointerDecay, + VK_RValue, /*BasePath=*/0, CCK).take(); break; default: @@ -2178,17 +2219,20 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, if (CheckExceptionSpecCompatibility(From, ToType)) return ExprError(); - From = ImpCastExprToType(From, ToType, CK_NoOp).take(); + From = ImpCastExprToType(From, ToType, CK_NoOp, + VK_RValue, /*BasePath=*/0, CCK).take(); break; case ICK_Integral_Promotion: case ICK_Integral_Conversion: - From = ImpCastExprToType(From, ToType, CK_IntegralCast).take(); + From = ImpCastExprToType(From, ToType, CK_IntegralCast, + VK_RValue, /*BasePath=*/0, CCK).take(); break; case ICK_Floating_Promotion: case ICK_Floating_Conversion: - From = ImpCastExprToType(From, ToType, CK_FloatingCast).take(); + From = ImpCastExprToType(From, ToType, CK_FloatingCast, + VK_RValue, /*BasePath=*/0, CCK).take(); break; case ICK_Complex_Promotion: @@ -2206,21 +2250,26 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, } else { CK = CK_IntegralComplexCast; } - From = ImpCastExprToType(From, ToType, CK).take(); + From = ImpCastExprToType(From, ToType, CK, + VK_RValue, /*BasePath=*/0, CCK).take(); break; } case ICK_Floating_Integral: if (ToType->isRealFloatingType()) - From = ImpCastExprToType(From, ToType, CK_IntegralToFloating).take(); + From = ImpCastExprToType(From, ToType, CK_IntegralToFloating, + VK_RValue, /*BasePath=*/0, CCK).take(); else - From = ImpCastExprToType(From, ToType, CK_FloatingToIntegral).take(); + From = ImpCastExprToType(From, ToType, CK_FloatingToIntegral, + VK_RValue, /*BasePath=*/0, CCK).take(); break; case ICK_Compatible_Conversion: - From = ImpCastExprToType(From, ToType, CK_NoOp).take(); + From = ImpCastExprToType(From, ToType, CK_NoOp, + VK_RValue, /*BasePath=*/0, CCK).take(); break; + case ICK_Writeback_Conversion: case ICK_Pointer_Conversion: { if (SCS.IncompatibleObjC && Action != AA_Casting) { // Diagnose incompatible Objective-C conversions @@ -2234,7 +2283,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, diag::ext_typecheck_convert_incompatible_pointer) << From->getType() << ToType << Action << From->getSourceRange(); - + if (From->getType()->isObjCObjectPointerType() && ToType->isObjCObjectPointerType()) EmitRelatedResultTypeNote(From); @@ -2244,7 +2293,8 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, CXXCastPath BasePath; if (CheckPointerConversion(From, ToType, Kind, BasePath, CStyle)) return ExprError(); - From = ImpCastExprToType(From, ToType, Kind, VK_RValue, &BasePath).take(); + From = ImpCastExprToType(From, ToType, Kind, VK_RValue, &BasePath, CCK) + .take(); break; } @@ -2255,13 +2305,15 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, return ExprError(); if (CheckExceptionSpecCompatibility(From, ToType)) return ExprError(); - From = ImpCastExprToType(From, ToType, Kind, VK_RValue, &BasePath).take(); + From = ImpCastExprToType(From, ToType, Kind, VK_RValue, &BasePath, CCK) + .take(); break; } case ICK_Boolean_Conversion: From = ImpCastExprToType(From, Context.BoolTy, - ScalarTypeToBooleanCastKind(FromType)).take(); + ScalarTypeToBooleanCastKind(FromType), + VK_RValue, /*BasePath=*/0, CCK).take(); break; case ICK_Derived_To_Base: { @@ -2276,16 +2328,18 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, From = ImpCastExprToType(From, ToType.getNonReferenceType(), CK_DerivedToBase, CastCategory(From), - &BasePath).take(); + &BasePath, CCK).take(); break; } case ICK_Vector_Conversion: - From = ImpCastExprToType(From, ToType, CK_BitCast).take(); + From = ImpCastExprToType(From, ToType, CK_BitCast, + VK_RValue, /*BasePath=*/0, CCK).take(); break; case ICK_Vector_Splat: - From = ImpCastExprToType(From, ToType, CK_VectorSplat).take(); + From = ImpCastExprToType(From, ToType, CK_VectorSplat, + VK_RValue, /*BasePath=*/0, CCK).take(); break; case ICK_Complex_Real: @@ -2321,27 +2375,30 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, // _Complex x -> x From = ImpCastExprToType(From, ElType, isFloatingComplex ? CK_FloatingComplexToReal - : CK_IntegralComplexToReal).take(); + : CK_IntegralComplexToReal, + VK_RValue, /*BasePath=*/0, CCK).take(); // x -> y if (Context.hasSameUnqualifiedType(ElType, ToType)) { // do nothing } else if (ToType->isRealFloatingType()) { From = ImpCastExprToType(From, ToType, - isFloatingComplex ? CK_FloatingCast : CK_IntegralToFloating).take(); + isFloatingComplex ? CK_FloatingCast : CK_IntegralToFloating, + VK_RValue, /*BasePath=*/0, CCK).take(); } else { assert(ToType->isIntegerType()); From = ImpCastExprToType(From, ToType, - isFloatingComplex ? CK_FloatingToIntegral : CK_IntegralCast).take(); + isFloatingComplex ? CK_FloatingToIntegral : CK_IntegralCast, + VK_RValue, /*BasePath=*/0, CCK).take(); } } break; case ICK_Block_Pointer_Conversion: { - From = ImpCastExprToType(From, ToType.getUnqualifiedType(), CK_BitCast, - VK_RValue).take(); - break; - } + From = ImpCastExprToType(From, ToType.getUnqualifiedType(), CK_BitCast, + VK_RValue, /*BasePath=*/0, CCK).take(); + break; + } case ICK_TransparentUnionConversion: { ExprResult FromRes = Owned(From); @@ -2376,7 +2433,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, ExprValueKind VK = ToType->isReferenceType() ? CastCategory(From) : VK_RValue; From = ImpCastExprToType(From, ToType.getNonLValueExprType(Context), - CK_NoOp, VK).take(); + CK_NoOp, VK, /*BasePath=*/0, CCK).take(); if (SCS.DeprecatedStringLiteralToCharPtr && !getLangOptions().WritableStrings) @@ -2553,6 +2610,23 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, case UTT_IsObject: return T->isObjectType(); case UTT_IsScalar: + // Note: semantic analysis depends on Objective-C lifetime types to be + // considered scalar types. However, such types do not actually behave + // like scalar types at run time (since they may require retain/release + // operations), so we report them as non-scalar. + if (T->isObjCLifetimeType()) { + switch (T.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + return true; + + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Weak: + case Qualifiers::OCL_Autoreleasing: + return false; + } + } + return T->isScalarType(); case UTT_IsCompound: return T->isCompoundType(); @@ -2566,13 +2640,13 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, case UTT_IsVolatile: return T.isVolatileQualified(); case UTT_IsTrivial: - return T->isTrivialType(); + return T.isTrivialType(Self.Context); case UTT_IsTriviallyCopyable: - return T->isTriviallyCopyableType(); + return T.isTriviallyCopyableType(Self.Context); case UTT_IsStandardLayout: return T->isStandardLayoutType(); case UTT_IsPOD: - return T->isPODType(); + return T.isPODType(Self.Context); case UTT_IsLiteral: return T->isLiteralType(); case UTT_IsEmpty: @@ -2605,7 +2679,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, // If __is_pod (type) is true then the trait is true, else if type is // a cv class or union type (or array thereof) with a trivial default // constructor ([class.ctor]) then the trait is true, else it is false. - if (T->isPODType()) + if (T.isPODType(Self.Context)) return true; if (const RecordType *RT = C.getBaseElementType(T)->getAs<RecordType>()) @@ -2617,7 +2691,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, // the trait is true, else if type is a cv class or union type // with a trivial copy constructor ([class.copy]) then the trait // is true, else it is false. - if (T->isPODType() || T->isReferenceType()) + if (T.isPODType(Self.Context) || T->isReferenceType()) return true; if (const RecordType *RT = T->getAs<RecordType>()) return cast<CXXRecordDecl>(RT->getDecl())->hasTrivialCopyConstructor(); @@ -2637,7 +2711,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, if (C.getBaseElementType(T).isConstQualified()) return false; - if (T->isPODType()) + if (T.isPODType(Self.Context)) return true; if (const RecordType *RT = T->getAs<RecordType>()) return cast<CXXRecordDecl>(RT->getDecl())->hasTrivialCopyAssignment(); @@ -2649,8 +2723,14 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, // type (or array thereof) with a trivial destructor // ([class.dtor]) then the trait is true, else it is // false. - if (T->isPODType() || T->isReferenceType()) + if (T.isPODType(Self.Context) || T->isReferenceType()) + return true; + + // Objective-C++ ARC: autorelease types don't require destruction. + if (T->isObjCLifetimeType() && + T.getObjCLifetime() == Qualifiers::OCL_Autoreleasing) return true; + if (const RecordType *RT = C.getBaseElementType(T)->getAs<RecordType>()) return cast<CXXRecordDecl>(RT->getDecl())->hasTrivialDestructor(); @@ -2668,8 +2748,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, return false; if (T->isReferenceType()) return false; - if (T->isPODType()) - return true; + if (T.isPODType(Self.Context) || T->isObjCLifetimeType()) + return true; if (const RecordType *RT = T->getAs<RecordType>()) { CXXRecordDecl* RD = cast<CXXRecordDecl>(RT->getDecl()); if (RD->hasTrivialCopyAssignment()) @@ -2704,7 +2784,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, // if type is a cv class or union type with copy constructors that are // known not to throw an exception then the trait is true, else it is // false. - if (T->isPODType() || T->isReferenceType()) + if (T.isPODType(C) || T->isReferenceType() || T->isObjCLifetimeType()) return true; if (const RecordType *RT = T->getAs<RecordType>()) { CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl()); @@ -2744,7 +2824,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, // true, else if type is a cv class or union type (or array // thereof) with a default constructor that is known not to // throw an exception then the trait is true, else it is false. - if (T->isPODType()) + if (T.isPODType(C) || T->isObjCLifetimeType()) return true; if (const RecordType *RT = C.getBaseElementType(T)->getAs<RecordType>()) { CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl()); @@ -3828,17 +3908,84 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) { if (!E) return ExprError(); - if (!Context.getLangOptions().CPlusPlus) + assert(!isa<CXXBindTemporaryExpr>(E) && "Double-bound temporary?"); + + // If the result is a glvalue, we shouldn't bind it. + if (!E->isRValue()) return Owned(E); - assert(!isa<CXXBindTemporaryExpr>(E) && "Double-bound temporary?"); + // In ARC, calls that return a retainable type can return retained, + // in which case we have to insert a consuming cast. + if (getLangOptions().ObjCAutoRefCount && + E->getType()->isObjCRetainableType()) { + + bool ReturnsRetained; + + // For actual calls, we compute this by examining the type of the + // called value. + if (CallExpr *Call = dyn_cast<CallExpr>(E)) { + Expr *Callee = Call->getCallee()->IgnoreParens(); + QualType T = Callee->getType(); + + if (T == Context.BoundMemberTy) { + // Handle pointer-to-members. + if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(Callee)) + T = BinOp->getRHS()->getType(); + else if (MemberExpr *Mem = dyn_cast<MemberExpr>(Callee)) + T = Mem->getMemberDecl()->getType(); + } + + if (const PointerType *Ptr = T->getAs<PointerType>()) + T = Ptr->getPointeeType(); + else if (const BlockPointerType *Ptr = T->getAs<BlockPointerType>()) + T = Ptr->getPointeeType(); + else if (const MemberPointerType *MemPtr = T->getAs<MemberPointerType>()) + T = MemPtr->getPointeeType(); + + const FunctionType *FTy = T->getAs<FunctionType>(); + assert(FTy && "call to value not of function type?"); + ReturnsRetained = FTy->getExtInfo().getProducesResult(); + + // ActOnStmtExpr arranges things so that StmtExprs of retainable + // type always produce a +1 object. + } else if (isa<StmtExpr>(E)) { + ReturnsRetained = true; + + // For message sends and property references, we try to find an + // actual method. FIXME: we should infer retention by selector in + // cases where we don't have an actual method. + } else { + Decl *D = 0; + if (ObjCMessageExpr *Send = dyn_cast<ObjCMessageExpr>(E)) { + D = Send->getMethodDecl(); + } else { + CastExpr *CE = cast<CastExpr>(E); + // FIXME. What other cast kinds to check for? + if (CE->getCastKind() == CK_ObjCProduceObject || + CE->getCastKind() == CK_LValueToRValue) + return MaybeBindToTemporary(CE->getSubExpr()); + assert(CE->getCastKind() == CK_GetObjCProperty); + const ObjCPropertyRefExpr *PRE = CE->getSubExpr()->getObjCProperty(); + D = (PRE->isImplicitProperty() ? PRE->getImplicitPropertyGetter() : 0); + } - const RecordType *RT = E->getType()->getAs<RecordType>(); - if (!RT) + ReturnsRetained = (D && D->hasAttr<NSReturnsRetainedAttr>()); + } + + if (ReturnsRetained) { + ExprNeedsCleanups = true; + E = ImplicitCastExpr::Create(Context, E->getType(), + CK_ObjCConsumeObject, E, 0, + VK_RValue); + } return Owned(E); + } - // If the result is a glvalue, we shouldn't bind it. - if (E->Classify(Context).isGLValue()) + if (!getLangOptions().CPlusPlus) + return Owned(E); + + const RecordType *RT = E->getType()->getAs<RecordType>(); + if (!RT) return Owned(E); // That should be enough to guarantee that this type is complete. @@ -3847,15 +3994,18 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) { if (RD->isInvalidDecl() || RD->hasTrivialDestructor()) return Owned(E); - CXXTemporary *Temp = CXXTemporary::Create(Context, LookupDestructor(RD)); - ExprTemporaries.push_back(Temp); - if (CXXDestructorDecl *Destructor = LookupDestructor(RD)) { + CXXDestructorDecl *Destructor = LookupDestructor(RD); + + CXXTemporary *Temp = CXXTemporary::Create(Context, Destructor); + if (Destructor) { MarkDeclarationReferenced(E->getExprLoc(), Destructor); CheckDestructorAccess(E->getExprLoc(), Destructor, PDiag(diag::err_access_dtor_temp) << E->getType()); + + ExprTemporaries.push_back(Temp); + ExprNeedsCleanups = true; } - // FIXME: Add the temporary to the temporaries vector. return Owned(CXXBindTemporaryExpr::Create(Context, Temp, E)); } @@ -3864,14 +4014,16 @@ Expr *Sema::MaybeCreateExprWithCleanups(Expr *SubExpr) { unsigned FirstTemporary = ExprEvalContexts.back().NumTemporaries; assert(ExprTemporaries.size() >= FirstTemporary); - if (ExprTemporaries.size() == FirstTemporary) + assert(ExprNeedsCleanups || ExprTemporaries.size() == FirstTemporary); + if (!ExprNeedsCleanups) return SubExpr; Expr *E = ExprWithCleanups::Create(Context, SubExpr, - &ExprTemporaries[FirstTemporary], + ExprTemporaries.begin() + FirstTemporary, ExprTemporaries.size() - FirstTemporary); ExprTemporaries.erase(ExprTemporaries.begin() + FirstTemporary, ExprTemporaries.end()); + ExprNeedsCleanups = false; return E; } @@ -3887,9 +4039,7 @@ Sema::MaybeCreateExprWithCleanups(ExprResult SubExpr) { Stmt *Sema::MaybeCreateStmtWithCleanups(Stmt *SubStmt) { assert(SubStmt && "sub statement can't be null!"); - unsigned FirstTemporary = ExprEvalContexts.back().NumTemporaries; - assert(ExprTemporaries.size() >= FirstTemporary); - if (ExprTemporaries.size() == FirstTemporary) + if (!ExprNeedsCleanups) return SubStmt; // FIXME: In order to attach the temporaries, wrap the statement into @@ -4047,17 +4197,35 @@ ExprResult Sema::BuildPseudoDestructorExpr(Expr *Base, QualType DestructedType = DestructedTypeInfo->getType(); SourceLocation DestructedTypeStart = DestructedTypeInfo->getTypeLoc().getLocalSourceRange().getBegin(); - if (!DestructedType->isDependentType() && !ObjectType->isDependentType() && - !Context.hasSameUnqualifiedType(DestructedType, ObjectType)) { - Diag(DestructedTypeStart, diag::err_pseudo_dtor_type_mismatch) - << ObjectType << DestructedType << Base->getSourceRange() - << DestructedTypeInfo->getTypeLoc().getLocalSourceRange(); - - // Recover by setting the destructed type to the object type. - DestructedType = ObjectType; - DestructedTypeInfo = Context.getTrivialTypeSourceInfo(ObjectType, + if (!DestructedType->isDependentType() && !ObjectType->isDependentType()) { + if (!Context.hasSameUnqualifiedType(DestructedType, ObjectType)) { + Diag(DestructedTypeStart, diag::err_pseudo_dtor_type_mismatch) + << ObjectType << DestructedType << Base->getSourceRange() + << DestructedTypeInfo->getTypeLoc().getLocalSourceRange(); + + // Recover by setting the destructed type to the object type. + DestructedType = ObjectType; + DestructedTypeInfo = Context.getTrivialTypeSourceInfo(ObjectType, DestructedTypeStart); - Destructed = PseudoDestructorTypeStorage(DestructedTypeInfo); + Destructed = PseudoDestructorTypeStorage(DestructedTypeInfo); + } else if (DestructedType.getObjCLifetime() != + ObjectType.getObjCLifetime()) { + + if (DestructedType.getObjCLifetime() == Qualifiers::OCL_None) { + // Okay: just pretend that the user provided the correctly-qualified + // type. + } else { + Diag(DestructedTypeStart, diag::err_arc_pseudo_dtor_inconstant_quals) + << ObjectType << DestructedType << Base->getSourceRange() + << DestructedTypeInfo->getTypeLoc().getLocalSourceRange(); + } + + // Recover by setting the destructed type to the object type. + DestructedType = ObjectType; + DestructedTypeInfo = Context.getTrivialTypeSourceInfo(ObjectType, + DestructedTypeStart); + Destructed = PseudoDestructorTypeStorage(DestructedTypeInfo); + } } } diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index cb5c1e0d0cb..1d1f6aeb348 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -19,6 +19,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/StmtVisitor.h" #include "clang/AST/TypeLoc.h" #include "llvm/ADT/SmallString.h" #include "clang/Lex/Preprocessor.h" @@ -182,6 +183,29 @@ ExprResult Sema::ParseObjCSelectorExpression(Selector Sel, if (Pos == ReferencedSelectors.end()) ReferencedSelectors.insert(std::make_pair(Sel, SelLoc)); + // In ARC, forbid the user from using @selector for + // retain/release/autorelease/dealloc/retainCount. + if (getLangOptions().ObjCAutoRefCount) { + switch (Sel.getMethodFamily()) { + case OMF_retain: + case OMF_release: + case OMF_autorelease: + case OMF_retainCount: + case OMF_dealloc: + Diag(AtLoc, diag::err_arc_illegal_selector) << + Sel << SourceRange(LParenLoc, RParenLoc); + break; + + case OMF_None: + case OMF_alloc: + case OMF_copy: + case OMF_init: + case OMF_mutableCopy: + case OMF_new: + case OMF_self: + break; + } + } QualType Ty = Context.getObjCSelType(); return new (Context) ObjCSelectorExpr(Ty, Sel, AtLoc, RParenLoc); } @@ -321,8 +345,12 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, Args[i] = Result.take(); } - unsigned DiagID = isClassMessage ? diag::warn_class_method_not_found : - diag::warn_inst_method_not_found; + unsigned DiagID; + if (getLangOptions().ObjCAutoRefCount) + DiagID = diag::err_arc_method_not_found; + else + DiagID = isClassMessage ? diag::warn_class_method_not_found + : diag::warn_inst_method_not_found; Diag(lbrac, DiagID) << Sel << isClassMessage << SourceRange(lbrac, rbrac); ReturnType = Context.getObjCIdType(); @@ -404,17 +432,15 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, return IsError; } -bool Sema::isSelfExpr(Expr *RExpr) { +bool Sema::isSelfExpr(Expr *receiver) { // 'self' is objc 'self' in an objc method only. DeclContext *DC = CurContext; while (isa<BlockDecl>(DC)) DC = DC->getParent(); if (DC && !isa<ObjCMethodDecl>(DC)) return false; - if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(RExpr)) - if (ICE->getCastKind() == CK_LValueToRValue) - RExpr = ICE->getSubExpr(); - if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(RExpr)) + receiver = receiver->IgnoreParenLValueCasts(); + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(receiver)) if (DRE->getDecl()->getIdentifier() == &Context.Idents.get("self")) return true; return false; @@ -1013,11 +1039,16 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, // Find the method we are messaging. if (!Method) { if (Class->isForwardDecl()) { + if (getLangOptions().ObjCAutoRefCount) { + Diag(Loc, diag::err_arc_receiver_forward_class) << ReceiverType; + } else { + Diag(Loc, diag::warn_receiver_forward_class) << Class->getDeclName(); + } + // A forward class used in messaging is treated as a 'Class' - Diag(Loc, diag::warn_receiver_forward_class) << Class->getDeclName(); Method = LookupFactoryMethodInGlobalPool(Sel, SourceRange(LBracLoc, RBracLoc)); - if (Method) + if (Method && !getLangOptions().ObjCAutoRefCount) Diag(Method->getLocation(), diag::note_method_sent_forward_class) << Method->getDeclName(); } @@ -1236,6 +1267,14 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, = ReceiverType->getAsObjCInterfacePointerType()) { // We allow sending a message to a pointer to an interface (an object). ClassDecl = OCIType->getInterfaceDecl(); + + if (ClassDecl->isForwardDecl() && getLangOptions().ObjCAutoRefCount) { + Diag(Loc, diag::err_arc_receiver_forward_instance) + << OCIType->getPointeeType() + << (Receiver ? Receiver->getSourceRange() : SourceRange(SuperLoc)); + return ExprError(); + } + // FIXME: consider using LookupInstanceMethodInGlobalPool, since it will be // faster than the following method (which can do *many* linear searches). // The idea is to add class info to MethodPool. @@ -1250,6 +1289,12 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, // If we have implementations in scope, check "private" methods. Method = LookupPrivateInstanceMethod(Sel, ClassDecl); + if (!Method && getLangOptions().ObjCAutoRefCount) { + Diag(Loc, diag::err_arc_may_not_respond) + << OCIType->getPointeeType() << Sel; + return ExprError(); + } + if (!Method && (!Receiver || !isSelfExpr(Receiver))) { // If we still haven't found a method, look in the global pool. This // behavior isn't very desirable, however we need it for GCC @@ -1267,10 +1312,12 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, } if (Method && DiagnoseUseOfDecl(Method, Loc, forwardClass)) return ExprError(); - } else if (!Context.getObjCIdType().isNull() && + } else if (!getLangOptions().ObjCAutoRefCount && + !Context.getObjCIdType().isNull() && (ReceiverType->isPointerType() || ReceiverType->isIntegerType())) { // Implicitly convert integers and pointers to 'id' but emit a warning. + // But not in ARC. Diag(Loc, diag::warn_bad_receiver_type) << ReceiverType << Receiver->getSourceRange(); @@ -1332,8 +1379,37 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, diag::err_illegal_message_expr_incomplete_type)) return ExprError(); + // In ARC, forbid the user from sending messages to + // retain/release/autorelease/dealloc/retainCount explicitly. + if (getLangOptions().ObjCAutoRefCount) { + ObjCMethodFamily family = + (Method ? Method->getMethodFamily() : Sel.getMethodFamily()); + switch (family) { + case OMF_init: + if (Method) + checkInitMethod(Method, ReceiverType); + + case OMF_None: + case OMF_alloc: + case OMF_copy: + case OMF_mutableCopy: + case OMF_new: + case OMF_self: + break; + + case OMF_dealloc: + case OMF_retain: + case OMF_release: + case OMF_autorelease: + case OMF_retainCount: + Diag(Loc, diag::err_arc_illegal_explicit_message) + << Sel << SelectorLoc; + break; + } + } + // Construct the appropriate ObjCMessageExpr instance. - Expr *Result; + ObjCMessageExpr *Result; if (SuperLoc.isValid()) Result = ObjCMessageExpr::Create(Context, ReturnType, VK, LBracLoc, SuperLoc, /*IsInstanceSuper=*/true, @@ -1343,6 +1419,27 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, Result = ObjCMessageExpr::Create(Context, ReturnType, VK, LBracLoc, Receiver, Sel, SelectorLoc, Method, Args, NumArgs, RBracLoc); + + if (getLangOptions().ObjCAutoRefCount) { + // In ARC, annotate delegate init calls. + if (Result->getMethodFamily() == OMF_init && + (SuperLoc.isValid() || isSelfExpr(Receiver))) { + // Only consider init calls *directly* in init implementations, + // not within blocks. + ObjCMethodDecl *method = dyn_cast<ObjCMethodDecl>(CurContext); + if (method && method->getMethodFamily() == OMF_init) { + // The implicit assignment to self means we also don't want to + // consume the result. + Result->setDelegateInitCall(true); + return Owned(Result); + } + } + + // In ARC, check for message sends which are likely to introduce + // retain cycles. + checkRetainCycles(Result); + } + return MaybeBindToTemporary(Result); } @@ -1364,3 +1461,293 @@ ExprResult Sema::ActOnInstanceMessage(Scope *S, LBracLoc, SelectorLoc, RBracLoc, move(Args)); } +enum ARCConversionTypeClass { + ACTC_none, + ACTC_retainable, + ACTC_indirectRetainable +}; +static ARCConversionTypeClass classifyTypeForARCConversion(QualType type) { + ARCConversionTypeClass ACTC = ACTC_retainable; + + // Ignore an outermost reference type. + if (const ReferenceType *ref = type->getAs<ReferenceType>()) + type = ref->getPointeeType(); + + // Drill through pointers and arrays recursively. + while (true) { + if (const PointerType *ptr = type->getAs<PointerType>()) { + type = ptr->getPointeeType(); + } else if (const ArrayType *array = type->getAsArrayTypeUnsafe()) { + type = QualType(array->getElementType()->getBaseElementTypeUnsafe(), 0); + } else { + break; + } + ACTC = ACTC_indirectRetainable; + } + + if (!type->isObjCRetainableType()) return ACTC_none; + return ACTC; +} + +namespace { + /// Return true if the given expression can be reasonably converted + /// between a retainable pointer type and a C pointer type. + struct ARCCastChecker : StmtVisitor<ARCCastChecker, bool> { + ASTContext &Context; + ARCCastChecker(ASTContext &Context) : Context(Context) {} + bool VisitStmt(Stmt *s) { + return false; + } + bool VisitExpr(Expr *e) { + return e->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull); + } + + bool VisitParenExpr(ParenExpr *e) { + return Visit(e->getSubExpr()); + } + bool VisitCastExpr(CastExpr *e) { + switch (e->getCastKind()) { + case CK_NullToPointer: + return true; + case CK_NoOp: + case CK_LValueToRValue: + case CK_BitCast: + case CK_AnyPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + return Visit(e->getSubExpr()); + default: + return false; + } + } + bool VisitUnaryExtension(UnaryOperator *e) { + return Visit(e->getSubExpr()); + } + bool VisitBinComma(BinaryOperator *e) { + return Visit(e->getRHS()); + } + bool VisitConditionalOperator(ConditionalOperator *e) { + // Conditional operators are okay if both sides are okay. + return Visit(e->getTrueExpr()) && Visit(e->getFalseExpr()); + } + bool VisitObjCStringLiteral(ObjCStringLiteral *e) { + // Always white-list Objective-C string literals. + return true; + } + bool VisitStmtExpr(StmtExpr *e) { + return Visit(e->getSubStmt()->body_back()); + } + bool VisitDeclRefExpr(DeclRefExpr *e) { + // White-list references to global extern strings from system + // headers. + if (VarDecl *var = dyn_cast<VarDecl>(e->getDecl())) + if (var->getStorageClass() == SC_Extern && + var->getType().isConstQualified() && + Context.getSourceManager().isInSystemHeader(var->getLocation())) + return true; + return false; + } + }; +} + +void +Sema::CheckObjCARCConversion(SourceRange castRange, QualType castType, + Expr *castExpr, CheckedConversionKind CCK) { + QualType castExprType = castExpr->getType(); + + ARCConversionTypeClass exprACTC = classifyTypeForARCConversion(castExprType); + ARCConversionTypeClass castACTC = classifyTypeForARCConversion(castType); + if (exprACTC == castACTC) return; + if (exprACTC && castType->isBooleanType()) return; + + // Allow casts between pointers to lifetime types (e.g., __strong id*) + // and pointers to void (e.g., cv void *). Casting from void* to lifetime* + // must be explicit. + if (const PointerType *CastPtr = castType->getAs<PointerType>()) { + if (const PointerType *CastExprPtr = castExprType->getAs<PointerType>()) { + QualType CastPointee = CastPtr->getPointeeType(); + QualType CastExprPointee = CastExprPtr->getPointeeType(); + if ((CCK != CCK_ImplicitConversion && + CastPointee->isObjCIndirectLifetimeType() && + CastExprPointee->isVoidType()) || + (CastPointee->isVoidType() && + CastExprPointee->isObjCIndirectLifetimeType())) + return; + } + } + + if (ARCCastChecker(Context).Visit(castExpr)) + return; + + SourceLocation loc = + (castRange.isValid() ? castRange.getBegin() : castExpr->getExprLoc()); + + if (makeUnavailableInSystemHeader(loc, + "converts between Objective-C and C pointers in -fobjc-arc")) + return; + + unsigned srcKind; + switch (exprACTC) { + case ACTC_none: + srcKind = (castExprType->isPointerType() ? 1 : 0); + break; + case ACTC_retainable: + srcKind = (castExprType->isBlockPointerType() ? 2 : 3); + break; + case ACTC_indirectRetainable: + srcKind = 4; + break; + } + + if (CCK == CCK_CStyleCast) { + // Check whether this could be fixed with a bridge cast. + SourceLocation AfterLParen = PP.getLocForEndOfToken(castRange.getBegin()); + SourceLocation NoteLoc = AfterLParen.isValid()? AfterLParen : loc; + + if (castType->isObjCARCBridgableType() && + castExprType->isCARCBridgableType()) { + Diag(loc, diag::err_arc_cast_requires_bridge) + << 2 + << castExprType + << (castType->isBlockPointerType()? 1 : 0) + << castType + << castRange + << castExpr->getSourceRange(); + Diag(NoteLoc, diag::note_arc_bridge) + << FixItHint::CreateInsertion(AfterLParen, "__bridge "); + Diag(NoteLoc, diag::note_arc_bridge_transfer) + << castExprType + << FixItHint::CreateInsertion(AfterLParen, "__bridge_transfer "); + + return; + } + + if (castType->isCARCBridgableType() && + castExprType->isObjCARCBridgableType()){ + Diag(loc, diag::err_arc_cast_requires_bridge) + << (castExprType->isBlockPointerType()? 1 : 0) + << castExprType + << 2 + << castType + << castRange + << castExpr->getSourceRange(); + + Diag(NoteLoc, diag::note_arc_bridge) + << FixItHint::CreateInsertion(AfterLParen, "__bridge "); + Diag(NoteLoc, diag::note_arc_bridge_retained) + << castType + << FixItHint::CreateInsertion(AfterLParen, "__bridge_retained "); + return; + } + } + + Diag(loc, diag::err_arc_mismatched_cast) + << (CCK != CCK_ImplicitConversion) << srcKind << castExprType << castType + << castRange << castExpr->getSourceRange(); +} + +ExprResult Sema::BuildObjCBridgedCast(SourceLocation LParenLoc, + ObjCBridgeCastKind Kind, + SourceLocation BridgeKeywordLoc, + TypeSourceInfo *TSInfo, + Expr *SubExpr) { + QualType T = TSInfo->getType(); + QualType FromType = SubExpr->getType(); + + bool MustConsume = false; + if (T->isDependentType() || SubExpr->isTypeDependent()) { + // Okay: we'll build a dependent expression type. + } else if (T->isObjCARCBridgableType() && FromType->isCARCBridgableType()) { + // Casting CF -> id + switch (Kind) { + case OBC_Bridge: + break; + + case OBC_BridgeRetained: + Diag(BridgeKeywordLoc, diag::err_arc_bridge_cast_wrong_kind) + << 2 + << FromType + << (T->isBlockPointerType()? 1 : 0) + << T + << SubExpr->getSourceRange() + << Kind; + Diag(BridgeKeywordLoc, diag::note_arc_bridge) + << FixItHint::CreateReplacement(BridgeKeywordLoc, "__bridge"); + Diag(BridgeKeywordLoc, diag::note_arc_bridge_transfer) + << FromType + << FixItHint::CreateReplacement(BridgeKeywordLoc, + "__bridge_transfer "); + + Kind = OBC_Bridge; + break; + + case OBC_BridgeTransfer: + // We must consume the Objective-C object produced by the cast. + MustConsume = true; + break; + } + } else if (T->isCARCBridgableType() && FromType->isObjCARCBridgableType()) { + // Okay: id -> CF + switch (Kind) { + case OBC_Bridge: + break; + + case OBC_BridgeRetained: + // Produce the object before casting it. + SubExpr = ImplicitCastExpr::Create(Context, FromType, + CK_ObjCProduceObject, + SubExpr, 0, VK_RValue); + break; + + case OBC_BridgeTransfer: + Diag(BridgeKeywordLoc, diag::err_arc_bridge_cast_wrong_kind) + << (FromType->isBlockPointerType()? 1 : 0) + << FromType + << 2 + << T + << SubExpr->getSourceRange() + << Kind; + + Diag(BridgeKeywordLoc, diag::note_arc_bridge) + << FixItHint::CreateReplacement(BridgeKeywordLoc, "__bridge "); + Diag(BridgeKeywordLoc, diag::note_arc_bridge_retained) + << T + << FixItHint::CreateReplacement(BridgeKeywordLoc, "__bridge_retained "); + + Kind = OBC_Bridge; + break; + } + } else { + Diag(LParenLoc, diag::err_arc_bridge_cast_incompatible) + << FromType << T << Kind + << SubExpr->getSourceRange() + << TSInfo->getTypeLoc().getSourceRange(); + return ExprError(); + } + + Expr *Result = new (Context) ObjCBridgedCastExpr(LParenLoc, Kind, + BridgeKeywordLoc, + TSInfo, SubExpr); + + if (MustConsume) { + ExprNeedsCleanups = true; + Result = ImplicitCastExpr::Create(Context, T, CK_ObjCConsumeObject, Result, + 0, VK_RValue); + } + + return Result; +} + +ExprResult Sema::ActOnObjCBridgedCast(Scope *S, + SourceLocation LParenLoc, + ObjCBridgeCastKind Kind, + SourceLocation BridgeKeywordLoc, + ParsedType Type, + SourceLocation RParenLoc, + Expr *SubExpr) { + TypeSourceInfo *TSInfo = 0; + QualType T = GetTypeFromParser(Type, &TSInfo); + if (!TSInfo) + TSInfo = Context.getTrivialTypeSourceInfo(T, LParenLoc); + return BuildObjCBridgedCast(LParenLoc, Kind, BridgeKeywordLoc, TSInfo, + SubExpr); +} diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index a33f5d0b2f3..77932151c1c 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -2029,10 +2029,10 @@ InitializedEntity InitializedEntity::InitializeBase(ASTContext &Context, DeclarationName InitializedEntity::getName() const { switch (getKind()) { - case EK_Parameter: - if (!VariableOrMember) - return DeclarationName(); - // Fall through + case EK_Parameter: { + ParmVarDecl *D = reinterpret_cast<ParmVarDecl*>(Parameter & ~0x1); + return (D ? D->getDeclName() : DeclarationName()); + } case EK_Variable: case EK_Member: @@ -2057,10 +2057,12 @@ DeclarationName InitializedEntity::getName() const { DeclaratorDecl *InitializedEntity::getDecl() const { switch (getKind()) { case EK_Variable: - case EK_Parameter: case EK_Member: return VariableOrMember; + case EK_Parameter: + return reinterpret_cast<ParmVarDecl*>(Parameter & ~0x1); + case EK_Result: case EK_Exception: case EK_New: @@ -2123,6 +2125,9 @@ void InitializationSequence::Step::Destroy() { case SK_StringInit: case SK_ObjCObjectConversion: case SK_ArrayInit: + case SK_PassByIndirectCopyRestore: + case SK_PassByIndirectRestore: + case SK_ProduceObjCObject: break; case SK_ConversionSequence: @@ -2306,6 +2311,22 @@ void InitializationSequence::AddArrayInitStep(QualType T) { Steps.push_back(S); } +void InitializationSequence::AddPassByIndirectCopyRestoreStep(QualType type, + bool shouldCopy) { + Step s; + s.Kind = (shouldCopy ? SK_PassByIndirectCopyRestore + : SK_PassByIndirectRestore); + s.Type = type; + Steps.push_back(s); +} + +void InitializationSequence::AddProduceObjCObjectStep(QualType T) { + Step S; + S.Kind = SK_ProduceObjCObject; + S.Type = T; + Steps.push_back(S); +} + void InitializationSequence::SetOverloadFailure(FailureKind Failure, OverloadingResult Result) { setSequenceKind(FailedSequence); @@ -2317,6 +2338,33 @@ void InitializationSequence::SetOverloadFailure(FailureKind Failure, // Attempt initialization //===----------------------------------------------------------------------===// +static void MaybeProduceObjCObject(Sema &S, + InitializationSequence &Sequence, + const InitializedEntity &Entity) { + if (!S.getLangOptions().ObjCAutoRefCount) return; + + /// When initializing a parameter, produce the value if it's marked + /// __attribute__((ns_consumed)). + if (Entity.getKind() == InitializedEntity::EK_Parameter) { + if (!Entity.isParameterConsumed()) + return; + + assert(Entity.getType()->isObjCRetainableType() && + "consuming an object of unretainable type?"); + Sequence.AddProduceObjCObjectStep(Entity.getType()); + + /// When initializing a return value, if the return type is a + /// retainable type, then returns need to immediately retain the + /// object. If an autorelease is required, it will be done at the + /// last instant. + } else if (Entity.getKind() == InitializedEntity::EK_Result) { + if (!Entity.getType()->isObjCRetainableType()) + return; + + Sequence.AddProduceObjCObjectStep(Entity.getType()); + } +} + /// \brief Attempt list initialization (C++0x [dcl.init.list]) static void TryListInitialization(Sema &S, const InitializedEntity &Entity, @@ -2380,13 +2428,16 @@ static OverloadingResult TryRefInitWithConversionFunction(Sema &S, bool DerivedToBase; bool ObjCConversion; + bool ObjCLifetimeConversion; assert(!S.CompareReferenceRelationship(Initializer->getLocStart(), T1, T2, DerivedToBase, - ObjCConversion) && + ObjCConversion, + ObjCLifetimeConversion) && "Must have incompatible references when binding via conversion"); (void)DerivedToBase; (void)ObjCConversion; - + (void)ObjCLifetimeConversion; + // Build the candidate set directly in the initialization sequence // structure, so that it will persist if we fail. OverloadCandidateSet &CandidateSet = Sequence.getFailedCandidateSet(); @@ -2513,10 +2564,12 @@ static OverloadingResult TryRefInitWithConversionFunction(Sema &S, bool NewDerivedToBase = false; bool NewObjCConversion = false; + bool NewObjCLifetimeConversion = false; Sema::ReferenceCompareResult NewRefRelationship = S.CompareReferenceRelationship(DeclLoc, T1, T2.getNonLValueExprType(S.Context), - NewDerivedToBase, NewObjCConversion); + NewDerivedToBase, NewObjCConversion, + NewObjCLifetimeConversion); if (NewRefRelationship == Sema::Ref_Incompatible) { // If the type we've converted to is not reference-related to the // type we're looking for, then there is another conversion step @@ -2584,10 +2637,11 @@ static void TryReferenceInitialization(Sema &S, bool isRValueRef = !isLValueRef; bool DerivedToBase = false; bool ObjCConversion = false; + bool ObjCLifetimeConversion = false; Expr::Classification InitCategory = Initializer->Classify(S.Context); Sema::ReferenceCompareResult RefRelationship = S.CompareReferenceRelationship(DeclLoc, cv1T1, cv2T2, DerivedToBase, - ObjCConversion); + ObjCConversion, ObjCLifetimeConversion); // C++0x [dcl.init.ref]p5: // A reference to type "cv1 T1" is initialized by an expression of type @@ -2746,11 +2800,15 @@ static void TryReferenceInitialization(Sema &S, InitializedEntity TempEntity = InitializedEntity::InitializeTemporary(cv1T1); - if (S.TryImplicitConversion(Sequence, TempEntity, Initializer, + ImplicitConversionSequence ICS + = S.TryImplicitConversion(Initializer, TempEntity.getType(), /*SuppressUserConversions*/ false, AllowExplicit, /*FIXME:InOverloadResolution=*/false, - /*CStyle=*/Kind.isCStyleOrFunctionalCast())) { + /*CStyle=*/Kind.isCStyleOrFunctionalCast(), + /*AllowObjCWritebackConversion=*/false); + + if (ICS.isBad()) { // FIXME: Use the conversion function set stored in ICS to turn // this into an overloading ambiguity diagnostic. However, we need // to keep that set as an OverloadCandidateSet rather than as some @@ -2764,6 +2822,8 @@ static void TryReferenceInitialization(Sema &S, else Sequence.SetFailed(InitializationSequence::FK_ReferenceInitFailed); return; + } else { + Sequence.AddConversionSequenceStep(ICS, TempEntity.getType()); } // [...] If T1 is reference-related to T2, cv1 must be the @@ -2953,10 +3013,8 @@ static void TryDefaultInitialization(Sema &S, // C++ [dcl.init]p6: // To default-initialize an object of type T means: // - if T is an array type, each element is default-initialized; - QualType DestType = Entity.getType(); - while (const ArrayType *Array = S.Context.getAsArrayType(DestType)) - DestType = Array->getElementType(); - + QualType DestType = S.Context.getBaseElementType(Entity.getType()); + // - if T is a (possibly cv-qualified) class type (Clause 9), the default // constructor for T is called (and the initialization is ill-formed if // T has no accessible default constructor); @@ -2970,8 +3028,16 @@ static void TryDefaultInitialization(Sema &S, // If a program calls for the default initialization of an object of // a const-qualified type T, T shall be a class type with a user-provided // default constructor. - if (DestType.isConstQualified() && S.getLangOptions().CPlusPlus) + if (DestType.isConstQualified() && S.getLangOptions().CPlusPlus) { Sequence.SetFailed(InitializationSequence::FK_DefaultInitOfConst); + return; + } + + // If the destination type has a lifetime property, zero-initialize it. + if (DestType.getQualifiers().hasObjCLifetime()) { + Sequence.AddZeroInitializationStep(Entity.getType()); + return; + } } /// \brief Attempt a user-defined conversion between two types (C++ [dcl.init]), @@ -3125,6 +3191,77 @@ static void TryUserDefinedConversion(Sema &S, } } +/// The non-zero enum values here are indexes into diagnostic alternatives. +enum InvalidICRKind { IIK_okay, IIK_nonlocal, IIK_nonscalar }; + +/// Determines whether this expression is an acceptable ICR source. +static InvalidICRKind isInvalidICRSource(ASTContext &C, Expr *e) { + // Skip parens. + e = e->IgnoreParens(); + + // Skip address-of nodes. + if (UnaryOperator *op = dyn_cast<UnaryOperator>(e)) { + if (op->getOpcode() == UO_AddrOf) + return isInvalidICRSource(C, op->getSubExpr()); + + // Skip certain casts. + } else if (CastExpr *cast = dyn_cast<CastExpr>(e)) { + switch (cast->getCastKind()) { + case CK_Dependent: + case CK_BitCast: + case CK_LValueBitCast: + case CK_LValueToRValue: + case CK_NoOp: + return isInvalidICRSource(C, cast->getSubExpr()); + + case CK_ArrayToPointerDecay: + return IIK_nonscalar; + + case CK_NullToPointer: + return IIK_okay; + + default: + break; + } + + // If we have a declaration reference, it had better be a local variable. + } else if (DeclRefExpr *declRef = dyn_cast<DeclRefExpr>(e)) { + if (VarDecl *var = dyn_cast<VarDecl>(declRef->getDecl())) + return (var->hasLocalStorage() ? IIK_okay : IIK_nonlocal); + + // If we have a conditional operator, check both sides. + } else if (ConditionalOperator *cond = dyn_cast<ConditionalOperator>(e)) { + if (InvalidICRKind iik = isInvalidICRSource(C, cond->getLHS())) + return iik; + + return isInvalidICRSource(C, cond->getRHS()); + + // These are never scalar. + } else if (isa<ArraySubscriptExpr>(e)) { + return IIK_nonscalar; + + // Otherwise, it needs to be a null pointer constant. + } else { + return (e->isNullPointerConstant(C, Expr::NPC_ValueDependentIsNull) + ? IIK_okay : IIK_nonlocal); + } + + return IIK_nonlocal; +} + +/// Check whether the given expression is a valid operand for an +/// indirect copy/restore. +static void checkIndirectCopyRestoreSource(Sema &S, Expr *src) { + assert(src->isRValue()); + + InvalidICRKind iik = isInvalidICRSource(S.Context, src); + if (iik == IIK_okay) return; + + S.Diag(src->getExprLoc(), diag::err_arc_nonlocal_writeback) + << ((unsigned) iik - 1) // shift index into diagnostic explanations + << src->getSourceRange(); +} + /// \brief Determine whether we have compatible array types for the /// purposes of GNU by-copy array initialization. static bool hasCompatibleArrayTypes(ASTContext &Context, @@ -3144,6 +3281,53 @@ static bool hasCompatibleArrayTypes(ASTContext &Context, return Source->isConstantArrayType() && Dest->isIncompleteArrayType(); } +static bool tryObjCWritebackConversion(Sema &S, + InitializationSequence &Sequence, + const InitializedEntity &Entity, + Expr *Initializer) { + bool ArrayDecay = false; + QualType ArgType = Initializer->getType(); + QualType ArgPointee; + if (const ArrayType *ArgArrayType = S.Context.getAsArrayType(ArgType)) { + ArrayDecay = true; + ArgPointee = ArgArrayType->getElementType(); + ArgType = S.Context.getPointerType(ArgPointee); + } + + // Handle write-back conversion. + QualType ConvertedArgType; + if (!S.isObjCWritebackConversion(ArgType, Entity.getType(), + ConvertedArgType)) + return false; + + // We should copy unless we're passing to an argument explicitly + // marked 'out'. + bool ShouldCopy = true; + if (ParmVarDecl *param = cast_or_null<ParmVarDecl>(Entity.getDecl())) + ShouldCopy = (param->getObjCDeclQualifier() != ParmVarDecl::OBJC_TQ_Out); + + // Do we need an lvalue conversion? + if (ArrayDecay || Initializer->isGLValue()) { + ImplicitConversionSequence ICS; + ICS.setStandard(); + ICS.Standard.setAsIdentityConversion(); + + QualType ResultType; + if (ArrayDecay) { + ICS.Standard.First = ICK_Array_To_Pointer; + ResultType = S.Context.getPointerType(ArgPointee); + } else { + ICS.Standard.First = ICK_Lvalue_To_Rvalue; + ResultType = Initializer->getType().getNonLValueExprType(S.Context); + } + + Sequence.AddConversionSequenceStep(ICS, ResultType); + } + + Sequence.AddPassByIndirectCopyRestoreStep(Entity.getType(), ShouldCopy); + return true; +} + InitializationSequence::InitializationSequence(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, @@ -3255,12 +3439,28 @@ InitializationSequence::InitializationSequence(Sema &S, return; } - // Handle initialization in C + // Determine whether we should consider writeback conversions for + // Objective-C ARC. + bool allowObjCWritebackConversion = S.getLangOptions().ObjCAutoRefCount && + Entity.getKind() == InitializedEntity::EK_Parameter; + + // We're at the end of the line for C: it's either a write-back conversion + // or it's a C assignment. There's no need to check anything else. if (!S.getLangOptions().CPlusPlus) { + // If allowed, check whether this is an Objective-C writeback conversion. + if (allowObjCWritebackConversion && + tryObjCWritebackConversion(S, *this, Entity, Initializer)) { + return; + } + + // Handle initialization in C AddCAssignmentStep(DestType); + MaybeProduceObjCObject(S, *this, Entity); return; } + assert(S.getLangOptions().CPlusPlus); + // - If the destination type is a (possibly cv-qualified) class type: if (DestType->isRecordType()) { // - If the initialization is direct-initialization, or if it is @@ -3294,6 +3494,7 @@ InitializationSequence::InitializationSequence(Sema &S, // type, conversion functions are considered. if (!SourceType.isNull() && SourceType->isRecordType()) { TryUserDefinedConversion(S, Entity, Kind, Initializer, *this); + MaybeProduceObjCObject(S, *this, Entity); return; } @@ -3302,12 +3503,38 @@ InitializationSequence::InitializationSequence(Sema &S, // conversions (Clause 4) will be used, if necessary, to convert the // initializer expression to the cv-unqualified version of the // destination type; no user-defined conversions are considered. - if (S.TryImplicitConversion(*this, Entity, Initializer, - /*SuppressUserConversions*/ true, + + ImplicitConversionSequence ICS + = S.TryImplicitConversion(Initializer, Entity.getType(), + /*SuppressUserConversions*/true, /*AllowExplicitConversions*/ false, /*InOverloadResolution*/ false, - /*CStyle=*/Kind.isCStyleOrFunctionalCast())) - { + /*CStyle=*/Kind.isCStyleOrFunctionalCast(), + allowObjCWritebackConversion); + + if (ICS.isStandard() && + ICS.Standard.Second == ICK_Writeback_Conversion) { + // Objective-C ARC writeback conversion. + + // We should copy unless we're passing to an argument explicitly + // marked 'out'. + bool ShouldCopy = true; + if (ParmVarDecl *Param = cast_or_null<ParmVarDecl>(Entity.getDecl())) + ShouldCopy = (Param->getObjCDeclQualifier() != ParmVarDecl::OBJC_TQ_Out); + + // If there was an lvalue adjustment, add it as a separate conversion. + if (ICS.Standard.First == ICK_Array_To_Pointer || + ICS.Standard.First == ICK_Lvalue_To_Rvalue) { + ImplicitConversionSequence LvalueICS; + LvalueICS.setStandard(); + LvalueICS.Standard.setAsIdentityConversion(); + LvalueICS.Standard.setAllToTypes(ICS.Standard.getToType(0)); + LvalueICS.Standard.First = ICS.Standard.First; + AddConversionSequenceStep(LvalueICS, ICS.Standard.getToType(0)); + } + + AddPassByIndirectCopyRestoreStep(Entity.getType(), ShouldCopy); + } else if (ICS.isBad()) { DeclAccessPair dap; if (Initializer->getType() == Context.OverloadTy && !S.ResolveAddressOfOverloadedFunction(Initializer @@ -3315,6 +3542,8 @@ InitializationSequence::InitializationSequence(Sema &S, SetFailed(InitializationSequence::FK_AddressOfOverloadFailed); else SetFailed(InitializationSequence::FK_ConversionFailed); + } else { + AddConversionSequenceStep(ICS, Entity.getType()); } } @@ -3560,7 +3789,7 @@ static ExprResult CopyObject(Sema &S, << (int)Entity.getKind() << CurInitExpr->getType() << CurInitExpr->getSourceRange(); S.Diag(Best->Function->getLocation(), diag::note_unavailable_here) - << Best->Function->isDeleted(); + << 1 << Best->Function->isDeleted(); return ExprError(); } @@ -3733,7 +3962,10 @@ InitializationSequence::Perform(Sema &S, case SK_CAssignment: case SK_StringInit: case SK_ObjCObjectConversion: - case SK_ArrayInit: { + case SK_ArrayInit: + case SK_PassByIndirectCopyRestore: + case SK_PassByIndirectRestore: + case SK_ProduceObjCObject: { assert(Args.size() == 1); CurInit = Args.get()[0]; if (!CurInit.get()) return ExprError(); @@ -3969,10 +4201,14 @@ InitializationSequence::Perform(Sema &S, } case SK_ConversionSequence: { + Sema::CheckedConversionKind CCK + = Kind.isCStyleCast()? Sema::CCK_CStyleCast + : Kind.isFunctionalCast()? Sema::CCK_FunctionalCast + : Kind.isExplicitCast()? Sema::CCK_OtherCast + : Sema::CCK_ImplicitConversion; ExprResult CurInitExprRes = S.PerformImplicitConversion(CurInit.get(), Step->Type, *Step->ICS, - getAssignmentAction(Entity), - Kind.isCStyleOrFunctionalCast()); + getAssignmentAction(Entity), CCK); if (CurInitExprRes.isInvalid()) return ExprError(); CurInit = move(CurInitExprRes); @@ -4182,7 +4418,20 @@ InitializationSequence::Perform(Sema &S, } } } + break; + case SK_PassByIndirectCopyRestore: + case SK_PassByIndirectRestore: + checkIndirectCopyRestoreSource(S, CurInit.get()); + CurInit = S.Owned(new (S.Context) + ObjCIndirectCopyRestoreExpr(CurInit.take(), Step->Type, + Step->Kind == SK_PassByIndirectCopyRestore)); + break; + + case SK_ProduceObjCObject: + CurInit = S.Owned(ImplicitCastExpr::Create(S.Context, Step->Type, + CK_ObjCProduceObject, + CurInit.take(), 0, VK_RValue)); break; } } @@ -4278,7 +4527,7 @@ bool InitializationSequence::Diagnose(Sema &S, true); if (Ovl == OR_Deleted) { S.Diag(Best->Function->getLocation(), diag::note_unavailable_here) - << Best->Function->isDeleted(); + << 1 << Best->Function->isDeleted(); } else { llvm_unreachable("Inconsistent overload resolution?"); } @@ -4443,7 +4692,7 @@ bool InitializationSequence::Diagnose(Sema &S, = FailedCandidateSet.BestViableFunction(S, Kind.getLocation(), Best); if (Ovl == OR_Deleted) { S.Diag(Best->Function->getLocation(), diag::note_unavailable_here) - << Best->Function->isDeleted(); + << 1 << Best->Function->isDeleted(); } else { llvm_unreachable("Inconsistent overload resolution?"); } @@ -4674,6 +4923,18 @@ void InitializationSequence::dump(llvm::raw_ostream &OS) const { case SK_ArrayInit: OS << "array initialization"; break; + + case SK_PassByIndirectCopyRestore: + OS << "pass by indirect copy and restore"; + break; + + case SK_PassByIndirectRestore: + OS << "pass by indirect restore"; + break; + + case SK_ProduceObjCObject: + OS << "Objective-C object retension"; + break; } } } diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp index 6c4469cef9e..2dbb7401ddb 100644 --- a/clang/lib/Sema/SemaObjCProperty.cpp +++ b/clang/lib/Sema/SemaObjCProperty.cpp @@ -24,6 +24,51 @@ using namespace clang; // Grammar actions. //===----------------------------------------------------------------------===// +/// Check the internal consistency of a property declaration. +static void checkARCPropertyDecl(Sema &S, ObjCPropertyDecl *property) { + if (property->isInvalidDecl()) return; + + ObjCPropertyDecl::PropertyAttributeKind propertyKind + = property->getPropertyAttributes(); + Qualifiers::ObjCLifetime propertyLifetime + = property->getType().getObjCLifetime(); + + // Nothing to do if we don't have a lifetime. + if (propertyLifetime == Qualifiers::OCL_None) return; + + Qualifiers::ObjCLifetime expectedLifetime; + unsigned selector; + + // Strong properties should have either strong or no lifetime. + if (propertyKind & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_strong | + ObjCPropertyDecl::OBJC_PR_copy)) { + expectedLifetime = Qualifiers::OCL_Strong; + selector = 0; + } else if (propertyKind & ObjCPropertyDecl::OBJC_PR_weak) { + expectedLifetime = Qualifiers::OCL_Weak; + selector = 1; + } else if (propertyKind & (ObjCPropertyDecl::OBJC_PR_assign | + ObjCPropertyDecl::OBJC_PR_unsafe_unretained) && + property->getType()->isObjCRetainableType()) { + expectedLifetime = Qualifiers::OCL_ExplicitNone; + selector = 2; + } else { + // We have a lifetime qualifier but no dominating property + // attribute. That's okay. + return; + } + + if (propertyLifetime == expectedLifetime) return; + + property->setInvalidDecl(); + S.Diag(property->getLocation(), + diag::err_arc_inconsistent_property_lifetime) + << property->getDeclName() + << selector + << propertyLifetime; +} + Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, FieldDeclarator &FD, ObjCDeclSpec &ODS, @@ -34,6 +79,14 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, tok::ObjCKeywordKind MethodImplKind, DeclContext *lexicalDC) { unsigned Attributes = ODS.getPropertyAttributes(); + TypeSourceInfo *TSI = GetTypeForDeclarator(FD.D, S); + QualType T = TSI->getType(); + if ((getLangOptions().getGCMode() != LangOptions::NonGC && + T.isObjCGCWeak()) || + (getLangOptions().ObjCAutoRefCount && + T.getObjCLifetime() == Qualifiers::OCL_Weak)) + Attributes |= ObjCDeclSpec::DQ_PR_weak; + bool isReadWrite = ((Attributes & ObjCDeclSpec::DQ_PR_readwrite) || // default is readwrite! !(Attributes & ObjCDeclSpec::DQ_PR_readonly)); @@ -42,9 +95,10 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, bool isAssign = ((Attributes & ObjCDeclSpec::DQ_PR_assign) || (isReadWrite && !(Attributes & ObjCDeclSpec::DQ_PR_retain) && - !(Attributes & ObjCDeclSpec::DQ_PR_copy))); - - TypeSourceInfo *TSI = GetTypeForDeclarator(FD.D, S); + !(Attributes & ObjCDeclSpec::DQ_PR_strong) && + !(Attributes & ObjCDeclSpec::DQ_PR_copy) && + !(Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained) && + !(Attributes & ObjCDeclSpec::DQ_PR_weak))); // Proceed with constructing the ObjCPropertDecls. ObjCContainerDecl *ClassDecl = @@ -58,20 +112,27 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, Attributes, isOverridingProperty, TSI, MethodImplKind); - if (Res) + if (Res) { CheckObjCPropertyAttributes(Res, AtLoc, Attributes); + if (getLangOptions().ObjCAutoRefCount) + checkARCPropertyDecl(*this, cast<ObjCPropertyDecl>(Res)); + } return Res; } - Decl *Res = CreatePropertyDecl(S, ClassDecl, AtLoc, FD, - GetterSel, SetterSel, - isAssign, isReadWrite, - Attributes, TSI, MethodImplKind); + ObjCPropertyDecl *Res = CreatePropertyDecl(S, ClassDecl, AtLoc, FD, + GetterSel, SetterSel, + isAssign, isReadWrite, + Attributes, TSI, MethodImplKind); if (lexicalDC) Res->setLexicalDeclContext(lexicalDC); // Validate the attributes on the @property. CheckObjCPropertyAttributes(Res, AtLoc, Attributes); + + if (getLangOptions().ObjCAutoRefCount) + checkARCPropertyDecl(*this, Res); + return Res; } @@ -158,6 +219,7 @@ Sema::HandlePropertyInClassExtension(Scope *S, ObjCCategoryDecl *CDecl, if (isReadWrite && (PIkind & ObjCPropertyDecl::OBJC_PR_readonly)) { unsigned retainCopyNonatomic = (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_strong | ObjCPropertyDecl::OBJC_PR_copy | ObjCPropertyDecl::OBJC_PR_nonatomic); if ((Attributes & retainCopyNonatomic) != @@ -189,6 +251,8 @@ Sema::HandlePropertyInClassExtension(Scope *S, ObjCCategoryDecl *CDecl, PIDecl->makeitReadWriteAttribute(); if (Attributes & ObjCDeclSpec::DQ_PR_retain) PIDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_retain); + if (Attributes & ObjCDeclSpec::DQ_PR_strong) + PIDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_strong); if (Attributes & ObjCDeclSpec::DQ_PR_copy) PIDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_copy); PIDecl->setSetterName(SetterSel); @@ -287,9 +351,18 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S, if (Attributes & ObjCDeclSpec::DQ_PR_retain) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_retain); + if (Attributes & ObjCDeclSpec::DQ_PR_strong) + PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_strong); + + if (Attributes & ObjCDeclSpec::DQ_PR_weak) + PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_weak); + if (Attributes & ObjCDeclSpec::DQ_PR_copy) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_copy); + if (Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained) + PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_unsafe_unretained); + if (isAssign) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_assign); @@ -298,8 +371,17 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S, else if (Attributes & ObjCDeclSpec::DQ_PR_atomic) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_atomic); + // FIXME: Why do PropertyAttributesAsWritten get set from PropertyAttributes, + // shouldn't PropertyAttributesAsWritten get set *only* through the attributes + // of the ObjCDeclSpec ? PDecl->setPropertyAttributesAsWritten(PDecl->getPropertyAttributes()); - + + // 'unsafe_unretained' is alias for 'assign'. + if (Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained) + PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_assign); + if (isAssign) + PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_unsafe_unretained); + if (MethodImplKind == tok::objc_required) PDecl->setPropertyImplementation(ObjCPropertyDecl::Required); else if (MethodImplKind == tok::objc_optional) @@ -308,6 +390,93 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S, return PDecl; } +static void checkARCPropertyImpl(Sema &S, SourceLocation propertyImplLoc, + ObjCPropertyDecl *property, + ObjCIvarDecl *ivar) { + if (property->isInvalidDecl() || ivar->isInvalidDecl()) return; + + QualType propertyType = property->getType(); + Qualifiers::ObjCLifetime propertyLifetime = propertyType.getObjCLifetime(); + ObjCPropertyDecl::PropertyAttributeKind propertyKind + = property->getPropertyAttributes(); + + QualType ivarType = ivar->getType(); + Qualifiers::ObjCLifetime ivarLifetime = ivarType.getObjCLifetime(); + + // Case 1: strong properties. + if (propertyLifetime == Qualifiers::OCL_Strong || + (propertyKind & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_strong | + ObjCPropertyDecl::OBJC_PR_copy))) { + switch (ivarLifetime) { + case Qualifiers::OCL_Strong: + // Okay. + return; + + case Qualifiers::OCL_None: + case Qualifiers::OCL_Autoreleasing: + // These aren't valid lifetimes for object ivars; don't diagnose twice. + return; + + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Weak: + S.Diag(propertyImplLoc, diag::err_arc_strong_property_lifetime) + << property->getDeclName() + << ivar->getDeclName() + << ivarLifetime; + break; + } + + // Case 2: weak properties. + } else if (propertyLifetime == Qualifiers::OCL_Weak || + (propertyKind & ObjCPropertyDecl::OBJC_PR_weak)) { + switch (ivarLifetime) { + case Qualifiers::OCL_Weak: + // Okay. + return; + + case Qualifiers::OCL_None: + case Qualifiers::OCL_Autoreleasing: + // These aren't valid lifetimes for object ivars; don't diagnose twice. + return; + + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Strong: + S.Diag(propertyImplLoc, diag::error_weak_property) + << property->getDeclName() + << ivar->getDeclName(); + break; + } + + // Case 3: assign properties. + } else if ((propertyKind & ObjCPropertyDecl::OBJC_PR_assign) && + propertyType->isObjCRetainableType()) { + switch (ivarLifetime) { + case Qualifiers::OCL_ExplicitNone: + // Okay. + return; + + case Qualifiers::OCL_None: + case Qualifiers::OCL_Autoreleasing: + // These aren't valid lifetimes for object ivars; don't diagnose twice. + return; + + case Qualifiers::OCL_Weak: + case Qualifiers::OCL_Strong: + S.Diag(propertyImplLoc, diag::err_arc_assign_property_lifetime) + << property->getDeclName() + << ivar->getDeclName(); + break; + } + + // Any other property should be ignored. + } else { + return; + } + + S.Diag(property->getLocation(), diag::note_property_declare); +} + /// ActOnPropertyImplDecl - This routine performs semantic checks and /// builds the AST node for a property implementation declaration; declared @@ -399,6 +568,8 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, // @synthesize if (!PropertyIvar) PropertyIvar = PropertyId; + ObjCPropertyDecl::PropertyAttributeKind kind + = property->getPropertyAttributes(); QualType PropType = Context.getCanonicalType(property->getType()); QualType PropertyIvarType = PropType; if (PropType->isReferenceType()) @@ -407,6 +578,45 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, ObjCInterfaceDecl *ClassDeclared; Ivar = IDecl->lookupInstanceVariable(PropertyIvar, ClassDeclared); if (!Ivar) { + // In ARC, give the ivar a lifetime qualifier based on its + // property attributes. + if (getLangOptions().ObjCAutoRefCount && + !PropertyIvarType.getObjCLifetime()) { + + // retain/copy have retaining lifetime. + if (kind & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_strong | + ObjCPropertyDecl::OBJC_PR_copy)) { + Qualifiers qs; + qs.addObjCLifetime(Qualifiers::OCL_Strong); + PropertyIvarType = Context.getQualifiedType(PropertyIvarType, qs); + } + else if (kind & ObjCPropertyDecl::OBJC_PR_weak) { + if (getLangOptions().ObjCNoAutoRefCountRuntime) { + Diag(PropertyLoc, diag::err_arc_weak_no_runtime); + Diag(property->getLocation(), diag::note_property_declare); + } + Qualifiers qs; + qs.addObjCLifetime(Qualifiers::OCL_Weak); + PropertyIvarType = Context.getQualifiedType(PropertyIvarType, qs); + } + else if (kind & ObjCPropertyDecl::OBJC_PR_assign && + PropertyIvarType->isObjCRetainableType()) { + // assume that an 'assign' property synthesizes __unsafe_unretained + // ivar + Qualifiers qs; + qs.addObjCLifetime(Qualifiers::OCL_ExplicitNone); + PropertyIvarType = Context.getQualifiedType(PropertyIvarType, qs); + } + } + + if (kind & ObjCPropertyDecl::OBJC_PR_weak && + !getLangOptions().ObjCAutoRefCount && + getLangOptions().getGCMode() == LangOptions::NonGC) { + Diag(PropertyLoc, diag::error_synthesize_weak_non_arc_or_gc); + Diag(property->getLocation(), diag::note_property_declare); + } + Ivar = ObjCIvarDecl::Create(Context, ClassImpDecl, PropertyLoc, PropertyLoc, PropertyIvar, PropertyIvarType, /*Dinfo=*/0, @@ -435,7 +645,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, if (PropertyIvarType != IvarType) { bool compat = false; if (isa<ObjCObjectPointerType>(PropertyIvarType) - && isa<ObjCObjectPointerType>(IvarType)) + && isa<ObjCObjectPointerType>(IvarType)) compat = Context.canAssignObjCInterfaces( PropertyIvarType->getAs<ObjCObjectPointerType>(), @@ -470,12 +680,13 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, // Fall thru - see previous comment } // __weak is explicit. So it works on Canonical type. - if (PropType.isObjCGCWeak() && !IvarType.isObjCGCWeak() && - getLangOptions().getGCMode() != LangOptions::NonGC) { + if ((PropType.isObjCGCWeak() && !IvarType.isObjCGCWeak() && + getLangOptions().getGCMode() != LangOptions::NonGC)) { Diag(PropertyLoc, diag::error_weak_property) << property->getDeclName() << Ivar->getDeclName(); // Fall thru - see previous comment } + // Fall thru - see previous comment if ((property->getType()->isObjCObjectPointerType() || PropType.isObjCGCStrong()) && IvarType.isObjCGCWeak() && getLangOptions().getGCMode() != LangOptions::NonGC) { @@ -484,9 +695,12 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, // Fall thru - see previous comment } } + if (getLangOptions().ObjCAutoRefCount) + checkARCPropertyImpl(*this, PropertyLoc, property, Ivar); } else if (PropertyIvar) // @dynamic Diag(PropertyLoc, diag::error_dynamic_property_ivar_decl); + assert (property && "ActOnPropertyImplDecl - property declaration missing"); ObjCPropertyImplDecl *PIDecl = ObjCPropertyImplDecl::Create(Context, CurContext, AtLoc, PropertyLoc, @@ -632,10 +846,19 @@ Sema::DiagnosePropertyMismatch(ObjCPropertyDecl *Property, != (SAttr & ObjCPropertyDecl::OBJC_PR_copy)) Diag(Property->getLocation(), diag::warn_property_attribute) << Property->getDeclName() << "copy" << inheritedName; - else if ((CAttr & ObjCPropertyDecl::OBJC_PR_retain) - != (SAttr & ObjCPropertyDecl::OBJC_PR_retain)) - Diag(Property->getLocation(), diag::warn_property_attribute) - << Property->getDeclName() << "retain" << inheritedName; + else { + unsigned CAttrRetain = + (CAttr & + (ObjCPropertyDecl::OBJC_PR_retain | ObjCPropertyDecl::OBJC_PR_strong)); + unsigned SAttrRetain = + (SAttr & + (ObjCPropertyDecl::OBJC_PR_retain | ObjCPropertyDecl::OBJC_PR_strong)); + bool CStrong = (CAttrRetain != 0); + bool SStrong = (SAttrRetain != 0); + if (CStrong != SStrong) + Diag(Property->getLocation(), diag::warn_property_attribute) + << Property->getDeclName() << "retain (or strong)" << inheritedName; + } if ((CAttr & ObjCPropertyDecl::OBJC_PR_nonatomic) != (SAttr & ObjCPropertyDecl::OBJC_PR_nonatomic)) @@ -1135,6 +1358,34 @@ Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl, } } +void Sema::DiagnoseOwningPropertyGetterSynthesis(const ObjCImplementationDecl *D) { + if (getLangOptions().getGCMode() == LangOptions::GCOnly) + return; + + for (ObjCImplementationDecl::propimpl_iterator + i = D->propimpl_begin(), e = D->propimpl_end(); i != e; ++i) { + ObjCPropertyImplDecl *PID = *i; + if (PID->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) + continue; + + const ObjCPropertyDecl *PD = PID->getPropertyDecl(); + if (PD && !D->getInstanceMethod(PD->getGetterName())) { + ObjCMethodDecl *method = PD->getGetterMethodDecl(); + if (!method) + continue; + ObjCMethodFamily family = method->getMethodFamily(); + if (family == OMF_alloc || family == OMF_copy || + family == OMF_mutableCopy || family == OMF_new) { + if (getLangOptions().ObjCAutoRefCount) + Diag(PID->getLocation(), diag::err_ownin_getter_rule); + else + Diag(PID->getLocation(), diag::warn_ownin_getter_rule); + Diag(PD->getLocation(), diag::note_property_declare); + } + } + } +} + /// AddPropertyAttrs - Propagates attributes from a property to the /// implicitly-declared getter or setter for that property. static void AddPropertyAttrs(Sema &S, ObjCMethodDecl *PropertyMethod, @@ -1245,7 +1496,7 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property, ParmVarDecl *Argument = ParmVarDecl::Create(Context, SetterMethod, Loc, Loc, property->getIdentifier(), - property->getType(), + property->getType().getUnqualifiedType(), /*TInfo=*/0, SC_None, SC_None, @@ -1287,7 +1538,7 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl, SourceLocation Loc, unsigned &Attributes) { // FIXME: Improve the reported location. - if (!PDecl) + if (!PDecl || PDecl->isInvalidDecl()) return; ObjCPropertyDecl *PropertyDecl = cast<ObjCPropertyDecl>(PDecl); @@ -1297,12 +1548,16 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl, if ((Attributes & ObjCDeclSpec::DQ_PR_readonly) && (Attributes & (ObjCDeclSpec::DQ_PR_readwrite | ObjCDeclSpec::DQ_PR_assign | + ObjCDeclSpec::DQ_PR_unsafe_unretained | ObjCDeclSpec::DQ_PR_copy | - ObjCDeclSpec::DQ_PR_retain))) { + ObjCDeclSpec::DQ_PR_retain | + ObjCDeclSpec::DQ_PR_strong))) { const char * which = (Attributes & ObjCDeclSpec::DQ_PR_readwrite) ? "readwrite" : (Attributes & ObjCDeclSpec::DQ_PR_assign) ? "assign" : + (Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained) ? + "unsafe_unretained" : (Attributes & ObjCDeclSpec::DQ_PR_copy) ? "copy" : "retain"; @@ -1313,14 +1568,15 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl, } // Check for copy or retain on non-object types. - if ((Attributes & (ObjCDeclSpec::DQ_PR_copy | ObjCDeclSpec::DQ_PR_retain)) && - !PropertyTy->isObjCObjectPointerType() && - !PropertyTy->isBlockPointerType() && - !Context.isObjCNSObjectType(PropertyTy) && + if ((Attributes & (ObjCDeclSpec::DQ_PR_weak | ObjCDeclSpec::DQ_PR_copy | + ObjCDeclSpec::DQ_PR_retain | ObjCDeclSpec::DQ_PR_strong)) && + !PropertyTy->isObjCRetainableType() && !PropertyDecl->getAttr<ObjCNSObjectAttr>()) { Diag(Loc, diag::err_objc_property_requires_object) - << (Attributes & ObjCDeclSpec::DQ_PR_copy ? "copy" : "retain"); - Attributes &= ~(ObjCDeclSpec::DQ_PR_copy | ObjCDeclSpec::DQ_PR_retain); + << (Attributes & ObjCDeclSpec::DQ_PR_weak ? "weak" : + Attributes & ObjCDeclSpec::DQ_PR_copy ? "copy" : "retain (or strong)"); + Attributes &= ~(ObjCDeclSpec::DQ_PR_weak | ObjCDeclSpec::DQ_PR_copy | + ObjCDeclSpec::DQ_PR_retain | ObjCDeclSpec::DQ_PR_strong); } // Check for more than one of { assign, copy, retain }. @@ -1335,27 +1591,88 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl, << "assign" << "retain"; Attributes &= ~ObjCDeclSpec::DQ_PR_retain; } + if (Attributes & ObjCDeclSpec::DQ_PR_strong) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "assign" << "strong"; + Attributes &= ~ObjCDeclSpec::DQ_PR_strong; + } + if (getLangOptions().ObjCAutoRefCount && + (Attributes & ObjCDeclSpec::DQ_PR_weak)) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "assign" << "weak"; + Attributes &= ~ObjCDeclSpec::DQ_PR_weak; + } + } else if (Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained) { + if (Attributes & ObjCDeclSpec::DQ_PR_copy) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "unsafe_unretained" << "copy"; + Attributes &= ~ObjCDeclSpec::DQ_PR_copy; + } + if (Attributes & ObjCDeclSpec::DQ_PR_retain) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "unsafe_unretained" << "retain"; + Attributes &= ~ObjCDeclSpec::DQ_PR_retain; + } + if (Attributes & ObjCDeclSpec::DQ_PR_strong) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "unsafe_unretained" << "strong"; + Attributes &= ~ObjCDeclSpec::DQ_PR_strong; + } + if (getLangOptions().ObjCAutoRefCount && + (Attributes & ObjCDeclSpec::DQ_PR_weak)) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "unsafe_unretained" << "weak"; + Attributes &= ~ObjCDeclSpec::DQ_PR_weak; + } } else if (Attributes & ObjCDeclSpec::DQ_PR_copy) { if (Attributes & ObjCDeclSpec::DQ_PR_retain) { Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) << "copy" << "retain"; Attributes &= ~ObjCDeclSpec::DQ_PR_retain; } + if (Attributes & ObjCDeclSpec::DQ_PR_strong) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "copy" << "strong"; + Attributes &= ~ObjCDeclSpec::DQ_PR_strong; + } + if (Attributes & ObjCDeclSpec::DQ_PR_weak) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "copy" << "weak"; + Attributes &= ~ObjCDeclSpec::DQ_PR_weak; + } + } + else if ((Attributes & ObjCDeclSpec::DQ_PR_retain) && + (Attributes & ObjCDeclSpec::DQ_PR_weak)) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "retain" << "weak"; + Attributes &= ~ObjCDeclSpec::DQ_PR_weak; + } + else if ((Attributes & ObjCDeclSpec::DQ_PR_strong) && + (Attributes & ObjCDeclSpec::DQ_PR_weak)) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "strong" << "weak"; + Attributes &= ~ObjCDeclSpec::DQ_PR_weak; } // Warn if user supplied no assignment attribute, property is // readwrite, and this is an object type. if (!(Attributes & (ObjCDeclSpec::DQ_PR_assign | ObjCDeclSpec::DQ_PR_copy | - ObjCDeclSpec::DQ_PR_retain)) && + ObjCDeclSpec::DQ_PR_unsafe_unretained | + ObjCDeclSpec::DQ_PR_retain | ObjCDeclSpec::DQ_PR_strong | + ObjCDeclSpec::DQ_PR_weak)) && !(Attributes & ObjCDeclSpec::DQ_PR_readonly) && PropertyTy->isObjCObjectPointerType()) { - // Skip this warning in gc-only mode. - if (getLangOptions().getGCMode() != LangOptions::GCOnly) - Diag(Loc, diag::warn_objc_property_no_assignment_attribute); - - // If non-gc code warn that this is likely inappropriate. - if (getLangOptions().getGCMode() == LangOptions::NonGC) - Diag(Loc, diag::warn_objc_property_default_assign_on_object); + if (getLangOptions().ObjCAutoRefCount) + Diag(Loc, diag::err_arc_objc_property_default_assign_on_object); + else { + // Skip this warning in gc-only mode. + if (getLangOptions().getGCMode() != LangOptions::GCOnly) + Diag(Loc, diag::warn_objc_property_no_assignment_attribute); + + // If non-gc code warn that this is likely inappropriate. + if (getLangOptions().getGCMode() == LangOptions::NonGC) + Diag(Loc, diag::warn_objc_property_default_assign_on_object); + } // FIXME: Implement warning dependent on NSCopying being // implemented. See also: diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 4bba6f8877b..2995e2e6e07 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -49,7 +49,8 @@ CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn, static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, bool InOverloadResolution, StandardConversionSequence &SCS, - bool CStyle); + bool CStyle, + bool AllowObjCWritebackConversion); static bool IsTransparentUnionStandardConversion(Sema &S, Expr* From, QualType &ToType, @@ -106,6 +107,7 @@ GetConversionCategory(ImplicitConversionKind Kind) { ICC_Conversion, ICC_Conversion, ICC_Conversion, + ICC_Conversion, ICC_Conversion }; return Category[(int)Kind]; @@ -138,7 +140,8 @@ ImplicitConversionRank GetConversionRank(ImplicitConversionKind Kind) { ICR_Conversion, ICR_Complex_Real_Conversion, ICR_Conversion, - ICR_Conversion + ICR_Conversion, + ICR_Writeback_Conversion }; return Rank[(int)Kind]; } @@ -170,6 +173,7 @@ const char* GetImplicitConversionName(ImplicitConversionKind Kind) { "Complex-real conversion", "Block Pointer conversion", "Transparent Union Conversion" + "Writeback conversion" }; return Name[Kind]; } @@ -181,12 +185,14 @@ void StandardConversionSequence::setAsIdentityConversion() { Second = ICK_Identity; Third = ICK_Identity; DeprecatedStringLiteralToCharPtr = false; + QualificationIncludesObjCLifetime = false; ReferenceBinding = false; DirectBinding = false; IsLvalueReference = true; BindsToFunctionLvalue = false; BindsToRvalue = false; BindsImplicitObjectArgumentWithoutRefQualifier = false; + ObjCLifetimeConversionBinding = false; CopyConstructor = 0; } @@ -753,15 +759,20 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old, /// not permitted. /// If @p AllowExplicit, then explicit user-defined conversions are /// permitted. +/// +/// \param AllowObjCWritebackConversion Whether we allow the Objective-C +/// writeback conversion, which allows __autoreleasing id* parameters to +/// be initialized with __strong id* or __weak id* arguments. static ImplicitConversionSequence TryImplicitConversion(Sema &S, Expr *From, QualType ToType, bool SuppressUserConversions, bool AllowExplicit, bool InOverloadResolution, - bool CStyle) { + bool CStyle, + bool AllowObjCWritebackConversion) { ImplicitConversionSequence ICS; if (IsStandardConversion(S, From, ToType, InOverloadResolution, - ICS.Standard, CStyle)) { + ICS.Standard, CStyle, AllowObjCWritebackConversion)){ ICS.setStandard(); return ICS; } @@ -867,24 +878,17 @@ TryImplicitConversion(Sema &S, Expr *From, QualType ToType, return ICS; } -bool Sema::TryImplicitConversion(InitializationSequence &Sequence, - const InitializedEntity &Entity, - Expr *Initializer, - bool SuppressUserConversions, - bool AllowExplicitConversions, - bool InOverloadResolution, - bool CStyle) { - ImplicitConversionSequence ICS - = clang::TryImplicitConversion(*this, Initializer, Entity.getType(), - SuppressUserConversions, - AllowExplicitConversions, - InOverloadResolution, - CStyle); - if (ICS.isBad()) return true; - - // Perform the actual conversion. - Sequence.AddConversionSequenceStep(ICS, Entity.getType()); - return false; +ImplicitConversionSequence +Sema::TryImplicitConversion(Expr *From, QualType ToType, + bool SuppressUserConversions, + bool AllowExplicit, + bool InOverloadResolution, + bool CStyle, + bool AllowObjCWritebackConversion) { + return clang::TryImplicitConversion(*this, From, ToType, + SuppressUserConversions, AllowExplicit, + InOverloadResolution, CStyle, + AllowObjCWritebackConversion); } /// PerformImplicitConversion - Perform an implicit conversion of the @@ -903,11 +907,18 @@ ExprResult Sema::PerformImplicitConversion(Expr *From, QualType ToType, AssignmentAction Action, bool AllowExplicit, ImplicitConversionSequence& ICS) { + // Objective-C ARC: Determine whether we will allow the writeback conversion. + bool AllowObjCWritebackConversion + = getLangOptions().ObjCAutoRefCount && + (Action == AA_Passing || Action == AA_Sending); + + ICS = clang::TryImplicitConversion(*this, From, ToType, /*SuppressUserConversions=*/false, AllowExplicit, /*InOverloadResolution=*/false, - /*CStyle=*/false); + /*CStyle=*/false, + AllowObjCWritebackConversion); return PerformImplicitConversion(From, ToType, ICS, Action); } @@ -1016,7 +1027,8 @@ static bool IsVectorConversion(ASTContext &Context, QualType FromType, static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, bool InOverloadResolution, StandardConversionSequence &SCS, - bool CStyle) { + bool CStyle, + bool AllowObjCWritebackConversion) { QualType FromType = From->getType(); // Standard conversions (C++ [conv]) @@ -1123,6 +1135,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, // conversion (4.4). (C++ 4.2p2) SCS.Second = ICK_Identity; SCS.Third = ICK_Qualification; + SCS.QualificationIncludesObjCLifetime = false; SCS.setAllToTypes(FromType); return true; } @@ -1199,7 +1212,10 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, SCS.Second = ICK_Floating_Integral; FromType = ToType.getUnqualifiedType(); } else if (S.IsBlockPointerConversion(FromType, ToType, FromType)) { - SCS.Second = ICK_Block_Pointer_Conversion; + SCS.Second = ICK_Block_Pointer_Conversion; + } else if (AllowObjCWritebackConversion && + S.isObjCWritebackConversion(FromType, ToType, FromType)) { + SCS.Second = ICK_Writeback_Conversion; } else if (S.IsPointerConversion(From, FromType, ToType, InOverloadResolution, FromType, IncompatibleObjC)) { // Pointer conversions (C++ 4.10). @@ -1235,8 +1251,11 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, QualType CanonFrom; QualType CanonTo; // The third conversion can be a qualification conversion (C++ 4p1). - if (S.IsQualificationConversion(FromType, ToType, CStyle)) { + bool ObjCLifetimeConversion; + if (S.IsQualificationConversion(FromType, ToType, CStyle, + ObjCLifetimeConversion)) { SCS.Third = ICK_Qualification; + SCS.QualificationIncludesObjCLifetime = ObjCLifetimeConversion; FromType = ToType; CanonFrom = S.Context.getCanonicalType(FromType); CanonTo = S.Context.getCanonicalType(ToType); @@ -1253,7 +1272,8 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, if (CanonFrom.getLocalUnqualifiedType() == CanonTo.getLocalUnqualifiedType() && (CanonFrom.getLocalCVRQualifiers() != CanonTo.getLocalCVRQualifiers() - || CanonFrom.getObjCGCAttr() != CanonTo.getObjCGCAttr())) { + || CanonFrom.getObjCGCAttr() != CanonTo.getObjCGCAttr() + || CanonFrom.getObjCLifetime() != CanonTo.getObjCLifetime())) { FromType = ToType; CanonFrom = CanonTo; } @@ -1284,7 +1304,8 @@ IsTransparentUnionStandardConversion(Sema &S, Expr* From, for (RecordDecl::field_iterator it = UD->field_begin(), itend = UD->field_end(); it != itend; ++it) { - if (IsStandardConversion(S, From, it->getType(), InOverloadResolution, SCS, CStyle)) { + if (IsStandardConversion(S, From, it->getType(), InOverloadResolution, SCS, + CStyle, /*ObjCWritebackConversion=*/false)) { ToType = it->getType(); return true; } @@ -1479,16 +1500,18 @@ bool Sema::IsComplexPromotion(QualType FromType, QualType ToType) { /// same type qualifiers as FromPtr has on its pointee type. ToType, /// if non-empty, will be a pointer to ToType that may or may not have /// the right set of qualifiers on its pointee. +/// static QualType BuildSimilarlyQualifiedPointerType(const Type *FromPtr, QualType ToPointee, QualType ToType, - ASTContext &Context) { + ASTContext &Context, + bool StripObjCLifetime = false) { assert((FromPtr->getTypeClass() == Type::Pointer || FromPtr->getTypeClass() == Type::ObjCObjectPointer) && "Invalid similarly-qualified pointer type"); - /// \brief Conversions to 'id' subsume cv-qualifier conversions. - if (ToType->isObjCIdType() || ToType->isObjCQualifiedIdType()) + /// Conversions to 'id' subsume cv-qualifier conversions. + if (ToType->isObjCIdType() || ToType->isObjCQualifiedIdType()) return ToType.getUnqualifiedType(); QualType CanonFromPointee @@ -1496,6 +1519,9 @@ BuildSimilarlyQualifiedPointerType(const Type *FromPtr, QualType CanonToPointee = Context.getCanonicalType(ToPointee); Qualifiers Quals = CanonFromPointee.getQualifiers(); + if (StripObjCLifetime) + Quals.removeObjCLifetime(); + // Exact qualifier match -> return the pointer type we're converting to. if (CanonToPointee.getLocalQualifiers() == Quals) { // ToType is exactly what we need. Return it. @@ -1599,7 +1625,8 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType, // Beyond this point, both types need to be pointers // , including objective-c pointers. QualType ToPointeeType = ToTypePtr->getPointeeType(); - if (FromType->isObjCObjectPointerType() && ToPointeeType->isVoidType()) { + if (FromType->isObjCObjectPointerType() && ToPointeeType->isVoidType() && + !getLangOptions().ObjCAutoRefCount) { ConvertedType = BuildSimilarlyQualifiedPointerType( FromType->getAs<ObjCObjectPointerType>(), ToPointeeType, @@ -1624,7 +1651,8 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType, ToPointeeType->isVoidType()) { ConvertedType = BuildSimilarlyQualifiedPointerType(FromTypePtr, ToPointeeType, - ToType, Context); + ToType, Context, + /*StripObjCLifetime=*/true); return true; } @@ -1814,6 +1842,7 @@ bool Sema::isObjCPointerConversion(QualType FromType, QualType ToType, ToPointeeType->getAs<ObjCObjectPointerType>() && isObjCPointerConversion(FromPointeeType, ToPointeeType, ConvertedType, IncompatibleObjC)) { + ConvertedType = Context.getPointerType(ConvertedType); ConvertedType = AdoptQualifiers(Context, ConvertedType, FromQualifiers); return true; @@ -1885,6 +1914,73 @@ bool Sema::isObjCPointerConversion(QualType FromType, QualType ToType, return false; } +/// \brief Determine whether this is an Objective-C writeback conversion, +/// used for parameter passing when performing automatic reference counting. +/// +/// \param FromType The type we're converting form. +/// +/// \param ToType The type we're converting to. +/// +/// \param ConvertedType The type that will be produced after applying +/// this conversion. +bool Sema::isObjCWritebackConversion(QualType FromType, QualType ToType, + QualType &ConvertedType) { + if (!getLangOptions().ObjCAutoRefCount || + Context.hasSameUnqualifiedType(FromType, ToType)) + return false; + + // Parameter must be a pointer to __autoreleasing (with no other qualifiers). + QualType ToPointee; + if (const PointerType *ToPointer = ToType->getAs<PointerType>()) + ToPointee = ToPointer->getPointeeType(); + else + return false; + + Qualifiers ToQuals = ToPointee.getQualifiers(); + if (!ToPointee->isObjCLifetimeType() || + ToQuals.getObjCLifetime() != Qualifiers::OCL_Autoreleasing || + !ToQuals.withoutObjCGLifetime().empty()) + return false; + + // Argument must be a pointer to __strong to __weak. + QualType FromPointee; + if (const PointerType *FromPointer = FromType->getAs<PointerType>()) + FromPointee = FromPointer->getPointeeType(); + else + return false; + + Qualifiers FromQuals = FromPointee.getQualifiers(); + if (!FromPointee->isObjCLifetimeType() || + (FromQuals.getObjCLifetime() != Qualifiers::OCL_Strong && + FromQuals.getObjCLifetime() != Qualifiers::OCL_Weak)) + return false; + + // Make sure that we have compatible qualifiers. + FromQuals.setObjCLifetime(Qualifiers::OCL_Autoreleasing); + if (!ToQuals.compatiblyIncludes(FromQuals)) + return false; + + // Remove qualifiers from the pointee type we're converting from; they + // aren't used in the compatibility check belong, and we'll be adding back + // qualifiers (with __autoreleasing) if the compatibility check succeeds. + FromPointee = FromPointee.getUnqualifiedType(); + + // The unqualified form of the pointee types must be compatible. + ToPointee = ToPointee.getUnqualifiedType(); + bool IncompatibleObjC; + if (Context.typesAreCompatible(FromPointee, ToPointee)) + FromPointee = ToPointee; + else if (!isObjCPointerConversion(FromPointee, ToPointee, FromPointee, + IncompatibleObjC)) + return false; + + /// \brief Construct the type we're converting to, which is a pointer to + /// __autoreleasing pointee. + FromPointee = Context.getQualifiedType(FromPointee, FromQuals); + ConvertedType = Context.getPointerType(FromPointee); + return true; +} + bool Sema::IsBlockPointerConversion(QualType FromType, QualType ToType, QualType& ConvertedType) { QualType ToPointeeType; @@ -2178,12 +2274,17 @@ bool Sema::CheckMemberPointerConversion(Expr *From, QualType ToType, /// IsQualificationConversion - Determines whether the conversion from /// an rvalue of type FromType to ToType is a qualification conversion /// (C++ 4.4). +/// +/// \param ObjCLifetimeConversion Output parameter that will be set to indicate +/// when the qualification conversion involves a change in the Objective-C +/// object lifetime. bool Sema::IsQualificationConversion(QualType FromType, QualType ToType, - bool CStyle) { + bool CStyle, bool &ObjCLifetimeConversion) { FromType = Context.getCanonicalType(FromType); ToType = Context.getCanonicalType(ToType); - + ObjCLifetimeConversion = false; + // If FromType and ToType are the same type, this is not a // qualification conversion. if (FromType.getUnqualifiedType() == ToType.getUnqualifiedType()) @@ -2206,6 +2307,21 @@ Sema::IsQualificationConversion(QualType FromType, QualType ToType, Qualifiers FromQuals = FromType.getQualifiers(); Qualifiers ToQuals = ToType.getQualifiers(); + // Objective-C ARC: + // Check Objective-C lifetime conversions. + if (FromQuals.getObjCLifetime() != ToQuals.getObjCLifetime() && + UnwrappedAnyPointer) { + if (ToQuals.compatiblyIncludesObjCLifetime(FromQuals)) { + ObjCLifetimeConversion = true; + FromQuals.removeObjCLifetime(); + ToQuals.removeObjCLifetime(); + } else { + // Qualification conversions cannot cast between different + // Objective-C lifetime qualifiers. + return false; + } + } + // Allow addition/removal of GC attributes but not changing GC attributes. if (FromQuals.getObjCGCAttr() != ToQuals.getObjCGCAttr() && (!FromQuals.hasObjCGCAttr() || !ToQuals.hasObjCGCAttr())) { @@ -2713,6 +2829,15 @@ CompareStandardConversionSequences(Sema &S, QualType UnqualT1 = S.Context.getUnqualifiedArrayType(T1, T1Quals); QualType UnqualT2 = S.Context.getUnqualifiedArrayType(T2, T2Quals); if (UnqualT1 == UnqualT2) { + // Objective-C++ ARC: If the references refer to objects with different + // lifetimes, prefer bindings that don't change lifetime. + if (SCS1.ObjCLifetimeConversionBinding != + SCS2.ObjCLifetimeConversionBinding) { + return SCS1.ObjCLifetimeConversionBinding + ? ImplicitConversionSequence::Worse + : ImplicitConversionSequence::Better; + } + // If the type is an array type, promote the element qualifiers to the // type for comparison. if (isa<ArrayType>(T1) && T1Quals) @@ -2722,7 +2847,7 @@ CompareStandardConversionSequences(Sema &S, if (T2.isMoreQualifiedThan(T1)) return ImplicitConversionSequence::Better; else if (T1.isMoreQualifiedThan(T2)) - return ImplicitConversionSequence::Worse; + return ImplicitConversionSequence::Worse; } } @@ -2770,6 +2895,17 @@ CompareQualificationConversions(Sema &S, ImplicitConversionSequence::CompareKind Result = ImplicitConversionSequence::Indistinguishable; + + // Objective-C++ ARC: + // Prefer qualification conversions not involving a change in lifetime + // to qualification conversions that do not change lifetime. + if (SCS1.QualificationIncludesObjCLifetime != + SCS2.QualificationIncludesObjCLifetime) { + Result = SCS1.QualificationIncludesObjCLifetime + ? ImplicitConversionSequence::Worse + : ImplicitConversionSequence::Better; + } + while (S.Context.UnwrapSimilarPointerTypes(T1, T2)) { // Within each iteration of the loop, we check the qualifiers to // determine if this still looks like a qualification @@ -3039,7 +3175,8 @@ Sema::ReferenceCompareResult Sema::CompareReferenceRelationship(SourceLocation Loc, QualType OrigT1, QualType OrigT2, bool &DerivedToBase, - bool &ObjCConversion) { + bool &ObjCConversion, + bool &ObjCLifetimeConversion) { assert(!OrigT1->isReferenceType() && "T1 must be the pointee type of the reference type"); assert(!OrigT2->isReferenceType() && "T2 cannot be a reference type"); @@ -3056,6 +3193,7 @@ Sema::CompareReferenceRelationship(SourceLocation Loc, // T1 is a base class of T2. DerivedToBase = false; ObjCConversion = false; + ObjCLifetimeConversion = false; if (UnqualT1 == UnqualT2) { // Nothing to do. } else if (!RequireCompleteType(Loc, OrigT2, PDiag()) && @@ -3090,9 +3228,16 @@ Sema::CompareReferenceRelationship(SourceLocation Loc, // qualifiers when performing these computations, so that e.g., an int in // address space 1 is not reference-compatible with an int in address // space 2. + if (T1Quals.getObjCLifetime() != T2Quals.getObjCLifetime() && + T1Quals.compatiblyIncludesObjCLifetime(T2Quals)) { + T1Quals.removeObjCLifetime(); + T2Quals.removeObjCLifetime(); + ObjCLifetimeConversion = true; + } + if (T1Quals == T2Quals) return Ref_Compatible; - else if (T1.isMoreQualifiedThan(T2)) + else if (T1Quals.compatiblyIncludes(T2Quals)) return Ref_Compatible_With_Added_Qualification; else return Ref_Related; @@ -3135,13 +3280,14 @@ FindConversionForRefInit(Sema &S, ImplicitConversionSequence &ICS, if (AllowRvalues) { bool DerivedToBase = false; bool ObjCConversion = false; + bool ObjCLifetimeConversion = false; if (!ConvTemplate && S.CompareReferenceRelationship( DeclLoc, Conv->getConversionType().getNonReferenceType() .getUnqualifiedType(), DeclType.getNonReferenceType().getUnqualifiedType(), - DerivedToBase, ObjCConversion) == + DerivedToBase, ObjCConversion, ObjCLifetimeConversion) == Sema::Ref_Incompatible) continue; } else { @@ -3242,10 +3388,11 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, bool isRValRef = DeclType->isRValueReferenceType(); bool DerivedToBase = false; bool ObjCConversion = false; + bool ObjCLifetimeConversion = false; Expr::Classification InitCategory = Init->Classify(S.Context); Sema::ReferenceCompareResult RefRelationship = S.CompareReferenceRelationship(DeclLoc, T1, T2, DerivedToBase, - ObjCConversion); + ObjCConversion, ObjCLifetimeConversion); // C++0x [dcl.init.ref]p5: @@ -3283,6 +3430,7 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, ICS.Standard.BindsToFunctionLvalue = T2->isFunctionType(); ICS.Standard.BindsToRvalue = false; ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false; + ICS.Standard.ObjCLifetimeConversionBinding = ObjCLifetimeConversion; ICS.Standard.CopyConstructor = 0; // Nothing more to do: the inaccessibility/ambiguity check for @@ -3328,7 +3476,7 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, // -- If the initializer expression // // -- is an xvalue, class prvalue, array prvalue or function - // lvalue and "cv1T1" is reference-compatible with "cv2 T2", or + // lvalue and "cv1 T1" is reference-compatible with "cv2 T2", or if (RefRelationship >= Sema::Ref_Compatible_With_Added_Qualification && (InitCategory.isXValue() || (InitCategory.isPRValue() && (T2->isRecordType() || T2->isArrayType())) || @@ -3356,6 +3504,7 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, ICS.Standard.BindsToFunctionLvalue = T2->isFunctionType(); ICS.Standard.BindsToRvalue = InitCategory.isRValue(); ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false; + ICS.Standard.ObjCLifetimeConversionBinding = ObjCLifetimeConversion; ICS.Standard.CopyConstructor = 0; return ICS; } @@ -3398,7 +3547,17 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, // we would be reference-compatible or reference-compatible with // added qualification. But that wasn't the case, so the reference // initialization fails. - return ICS; + // + // Note that we only want to check address spaces and cvr-qualifiers here. + // ObjC GC and lifetime qualifiers aren't important. + Qualifiers T1Quals = T1.getQualifiers(); + Qualifiers T2Quals = T2.getQualifiers(); + T1Quals.removeObjCGCAttr(); + T1Quals.removeObjCLifetime(); + T2Quals.removeObjCGCAttr(); + T2Quals.removeObjCLifetime(); + if (!T1Quals.compatiblyIncludes(T2Quals)) + return ICS; } // If at least one of the types is a class type, the types are not @@ -3429,7 +3588,8 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, ICS = TryImplicitConversion(S, Init, T1, SuppressUserConversions, /*AllowExplicit=*/false, /*InOverloadResolution=*/false, - /*CStyle=*/false); + /*CStyle=*/false, + /*AllowObjCWritebackConversion=*/false); // Of course, that's still a reference binding. if (ICS.isStandard()) { @@ -3438,12 +3598,14 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, ICS.Standard.BindsToFunctionLvalue = T2->isFunctionType(); ICS.Standard.BindsToRvalue = true; ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false; + ICS.Standard.ObjCLifetimeConversionBinding = false; } else if (ICS.isUserDefined()) { ICS.UserDefined.After.ReferenceBinding = true; ICS.Standard.IsLvalueReference = !isRValRef; ICS.Standard.BindsToFunctionLvalue = T2->isFunctionType(); ICS.Standard.BindsToRvalue = true; ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false; + ICS.Standard.ObjCLifetimeConversionBinding = false; } return ICS; @@ -3458,7 +3620,8 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, static ImplicitConversionSequence TryCopyInitialization(Sema &S, Expr *From, QualType ToType, bool SuppressUserConversions, - bool InOverloadResolution) { + bool InOverloadResolution, + bool AllowObjCWritebackConversion) { if (ToType->isReferenceType()) return TryReferenceInit(S, From, ToType, /*FIXME:*/From->getLocStart(), @@ -3469,7 +3632,8 @@ TryCopyInitialization(Sema &S, Expr *From, QualType ToType, SuppressUserConversions, /*AllowExplicit=*/false, InOverloadResolution, - /*CStyle=*/false); + /*CStyle=*/false, + AllowObjCWritebackConversion); } /// TryObjectArgumentInitialization - Try to initialize the object @@ -3659,7 +3823,8 @@ TryContextuallyConvertToBool(Sema &S, Expr *From) { /*SuppressUserConversions=*/false, /*AllowExplicit=*/true, /*InOverloadResolution=*/false, - /*CStyle=*/false); + /*CStyle=*/false, + /*AllowObjCWritebackConversion=*/false); } /// PerformContextuallyConvertToBool - Perform a contextual conversion @@ -3686,7 +3851,8 @@ TryContextuallyConvertToObjCId(Sema &S, Expr *From) { /*SuppressUserConversions=*/false, /*AllowExplicit=*/true, /*InOverloadResolution=*/false, - /*CStyle=*/false); + /*CStyle=*/false, + /*AllowObjCWritebackConversion=*/false); } /// PerformContextuallyConvertToObjCId - Perform a contextual conversion @@ -3980,7 +4146,9 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, Candidate.Conversions[ArgIdx] = TryCopyInitialization(*this, Args[ArgIdx], ParamType, SuppressUserConversions, - /*InOverloadResolution=*/true); + /*InOverloadResolution=*/true, + /*AllowObjCWritebackConversion=*/ + getLangOptions().ObjCAutoRefCount); if (Candidate.Conversions[ArgIdx].isBad()) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_bad_conversion; @@ -4153,7 +4321,9 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, Candidate.Conversions[ArgIdx + 1] = TryCopyInitialization(*this, Args[ArgIdx], ParamType, SuppressUserConversions, - /*InOverloadResolution=*/true); + /*InOverloadResolution=*/true, + /*AllowObjCWritebackConversion=*/ + getLangOptions().ObjCAutoRefCount); if (Candidate.Conversions[ArgIdx + 1].isBad()) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_bad_conversion; @@ -4374,7 +4544,8 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion, ImplicitConversionSequence ICS = TryCopyInitialization(*this, &Call, ToType, /*SuppressUserConversions=*/true, - /*InOverloadResolution=*/false); + /*InOverloadResolution=*/false, + /*AllowObjCWritebackConversion=*/false); switch (ICS.getKind()) { case ImplicitConversionSequence::StandardConversion: @@ -4544,7 +4715,9 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion, Candidate.Conversions[ArgIdx + 1] = TryCopyInitialization(*this, Args[ArgIdx], ParamType, /*SuppressUserConversions=*/false, - /*InOverloadResolution=*/false); + /*InOverloadResolution=*/false, + /*AllowObjCWritebackConversion=*/ + getLangOptions().ObjCAutoRefCount); if (Candidate.Conversions[ArgIdx + 1].isBad()) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_bad_conversion; @@ -4662,7 +4835,9 @@ void Sema::AddBuiltinCandidate(QualType ResultTy, QualType *ParamTys, Candidate.Conversions[ArgIdx] = TryCopyInitialization(*this, Args[ArgIdx], ParamTys[ArgIdx], ArgIdx == 0 && IsAssignmentOperator, - /*InOverloadResolution=*/false); + /*InOverloadResolution=*/false, + /*AllowObjCWritebackConversion=*/ + getLangOptions().ObjCAutoRefCount); } if (Candidate.Conversions[ArgIdx].isBad()) { Candidate.Viable = false; @@ -6619,6 +6794,17 @@ void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, unsigned I) { return; } + if (FromQs.getObjCLifetime() != ToQs.getObjCLifetime()) { + S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_lifetime) + << (unsigned) FnKind << FnDesc + << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) + << FromTy + << FromQs.getObjCLifetime() << ToQs.getObjCLifetime() + << (unsigned) isObjectArgument << I+1; + MaybeEmitInheritedConstructorNote(S, Fn); + return; + } + if (FromQs.getObjCGCAttr() != ToQs.getObjCGCAttr()) { S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_gc) << (unsigned) FnKind << FnDesc @@ -7152,7 +7338,9 @@ void CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand, = TryCopyInitialization(S, Args[ConvIdx], Cand->BuiltinTypes.ParamTypes[ConvIdx], SuppressUserConversions, - /*InOverloadResolution*/ true); + /*InOverloadResolution*/ true, + /*AllowObjCWritebackConversion=*/ + S.getLangOptions().ObjCAutoRefCount); return; } @@ -7163,7 +7351,9 @@ void CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand, Cand->Conversions[ConvIdx] = TryCopyInitialization(S, Args[ArgIdx], Proto->getArgType(ArgIdx), SuppressUserConversions, - /*InOverloadResolution=*/true); + /*InOverloadResolution=*/true, + /*AllowObjCWritebackConversion=*/ + S.getLangOptions().ObjCAutoRefCount); else Cand->Conversions[ConvIdx].setEllipsis(); } diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index d7c0a543eed..18e5f7e3a46 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -66,8 +66,29 @@ void Sema::ActOnForEachDeclStmt(DeclGroupPtrTy dg) { // If we have an invalid decl, just return. if (DG.isNull() || !DG.isSingleDecl()) return; + VarDecl *var = cast<VarDecl>(DG.getSingleDecl()); + // suppress any potential 'unused variable' warning. - DG.getSingleDecl()->setUsed(); + var->setUsed(); + + // In ARC, we don't want to lifetime for the iteration + // variable of a fast enumeration loop. Rather than actually + // trying to catch that during declaration processing, we + // remove the consequences here. + if (getLangOptions().ObjCAutoRefCount) { + SplitQualType split = var->getType().split(); + + // Inferred lifetime will show up as a local qualifier because + // explicit lifetime would have shown up as an AttributedType + // instead. + if (split.second.hasObjCLifetime()) { + // Change the qualification to 'const __unsafe_unretained'. + split.second.setObjCLifetime(Qualifiers::OCL_ExplicitNone); + split.second.addConst(); + var->setType(Context.getQualifiedType(split.first, split.second)); + var->setInit(0); + } + } } void Sema::DiagnoseUnusedExprResult(const Stmt *S) { @@ -114,6 +135,10 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) { } } } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) { + if (getLangOptions().ObjCAutoRefCount && ME->isDelegateInitCall()) { + Diag(Loc, diag::err_arc_unused_init_message) << R1; + return; + } const ObjCMethodDecl *MD = ME->getMethodDecl(); if (MD && MD->getAttr<WarnUnusedResultAttr>()) { Diag(Loc, diag::warn_unused_call) << R1 << R2 << "warn_unused_result"; @@ -951,14 +976,13 @@ Sema::ActOnObjCForCollectionStmt(SourceLocation ForLoc, return StmtError(Diag((*DS->decl_begin())->getLocation(), diag::err_toomany_element_decls)); - Decl *D = DS->getSingleDecl(); - FirstType = cast<ValueDecl>(D)->getType(); + VarDecl *D = cast<VarDecl>(DS->getSingleDecl()); + FirstType = D->getType(); // C99 6.8.5p3: The declaration part of a 'for' statement shall only // declare identifiers for objects having storage class 'auto' or // 'register'. - VarDecl *VD = cast<VarDecl>(D); - if (VD->isLocalVarDecl() && !VD->hasLocalStorage()) - return StmtError(Diag(VD->getLocation(), + if (!D->hasLocalStorage()) + return StmtError(Diag(D->getLocation(), diag::err_non_variable_decl_in_for)); } else { Expr *FirstE = cast<Expr>(First); @@ -1047,6 +1071,13 @@ static bool FinishForRangeVarDecl(Sema &SemaRef, VarDecl *Decl, Expr *Init, Decl->setTypeSourceInfo(InitTSI); Decl->setType(InitTSI->getType()); + // In ARC, infer lifetime. + // FIXME: ARC may want to turn this into 'const __unsafe_unretained' if + // we're doing the equivalent of fast iteration. + if (SemaRef.getLangOptions().ObjCAutoRefCount && + SemaRef.inferObjCARCLifetime(Decl)) + Decl->setInvalidDecl(); + SemaRef.AddInitializerToDecl(Decl, Init, /*DirectInit=*/false, /*TypeMayContainAuto=*/false); SemaRef.FinalizeDeclaration(Decl); @@ -1797,7 +1828,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { if (getLangOptions().CPlusPlus && FnRetType->isRecordType() && !CurContext->isDependentContext()) FunctionScopes.back()->Returns.push_back(Result); - + return Owned(Result); } @@ -2179,6 +2210,12 @@ Sema::ActOnCXXCatchBlock(SourceLocation CatchLoc, Decl *ExDecl, HandlerBlock)); } +StmtResult +Sema::ActOnObjCAutoreleasePoolStmt(SourceLocation AtLoc, Stmt *Body) { + getCurFunction()->setHasBranchProtectedScope(); + return Owned(new (Context) ObjCAutoreleasePoolStmt(AtLoc, Body)); +} + namespace { class TypeWithHandler { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index d458050585f..b8caad8af55 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -3503,9 +3503,11 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S, return true; } + bool ObjCLifetimeConversion; if (ParamType->isPointerType() && !ParamType->getAs<PointerType>()->getPointeeType()->isFunctionType() && - S.IsQualificationConversion(ArgType, ParamType, false)) { + S.IsQualificationConversion(ArgType, ParamType, false, + ObjCLifetimeConversion)) { // For pointer-to-object types, qualification conversions are // permitted. } else { @@ -3865,8 +3867,9 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, return Owned(Arg); } + bool ObjCLifetimeConversion; if (IsQualificationConversion(ArgType, ParamType.getNonReferenceType(), - false)) { + false, ObjCLifetimeConversion)) { Arg = ImpCastExprToType(Arg, ParamType, CK_NoOp, CastCategory(Arg)).take(); } else if (!Context.hasSameUnqualifiedType(ArgType, ParamType.getNonReferenceType())) { @@ -3933,9 +3936,11 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // member, qualification conversions (4.4) are applied. assert(ParamType->isMemberPointerType() && "Only pointers to members remain"); + bool ObjCLifetimeConversion; if (Context.hasSameUnqualifiedType(ParamType, ArgType)) { // Types match exactly: nothing more to do here. - } else if (IsQualificationConversion(ArgType, ParamType, false)) { + } else if (IsQualificationConversion(ArgType, ParamType, false, + ObjCLifetimeConversion)) { Arg = ImpCastExprToType(Arg, ParamType, CK_NoOp, CastCategory(Arg)).take(); } else { // We can't perform this conversion. @@ -4043,8 +4048,10 @@ Sema::BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg, // We might need to perform a trailing qualification conversion, since // the element type on the parameter could be more qualified than the // element type in the expression we constructed. + bool ObjCLifetimeConversion; if (IsQualificationConversion(((Expr*) RefExpr.get())->getType(), - ParamType.getUnqualifiedType(), false)) + ParamType.getUnqualifiedType(), false, + ObjCLifetimeConversion)) RefExpr = ImpCastExprToType(RefExpr.take(), ParamType.getUnqualifiedType(), CK_NoOp); assert(!RefExpr.isInvalid() && diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 9011cdf7536..359c93130a0 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -819,6 +819,11 @@ static bool hasInconsistentOrSupersetQualifiersOf(QualType ParamType, ParamQs.hasAddressSpace()) return true; + // Mismatched (but not missing) Objective-C lifetime qualifiers. + if (ParamQs.getObjCLifetime() != ArgQs.getObjCLifetime() && + ParamQs.hasObjCLifetime()) + return true; + // CVR qualifier superset. return (ParamQs.getCVRQualifiers() != ArgQs.getCVRQualifiers()) && ((ParamQs.getCVRQualifiers() | ArgQs.getCVRQualifiers()) @@ -1009,6 +1014,8 @@ DeduceTemplateArguments(Sema &S, DeducedQs.removeObjCGCAttr(); if (ParamQs.hasAddressSpace()) DeducedQs.removeAddressSpace(); + if (ParamQs.hasObjCLifetime()) + DeducedQs.removeObjCLifetime(); DeducedType = S.Context.getQualifiedType(DeducedType.getUnqualifiedType(), DeducedQs); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index ef3c2624b2c..bfffb9e41bb 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -26,6 +26,7 @@ #include "clang/Basic/TargetInfo.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/DeclSpec.h" +#include "clang/Sema/DelayedDiagnostic.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Support/ErrorHandling.h" using namespace clang; @@ -110,7 +111,8 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr, // objc_gc applies to Objective-C pointers or, otherwise, to the // smallest available pointer type (i.e. 'void*' in 'void**'). #define OBJC_POINTER_TYPE_ATTRS_CASELIST \ - case AttributeList::AT_objc_gc + case AttributeList::AT_objc_gc: \ + case AttributeList::AT_objc_lifetime // Function type attributes. #define FUNCTION_TYPE_ATTRS_CASELIST \ @@ -295,11 +297,15 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, static bool handleObjCGCTypeAttr(TypeProcessingState &state, AttributeList &attr, QualType &type); +static bool handleObjCLifetimeTypeAttr(TypeProcessingState &state, + AttributeList &attr, QualType &type); + static bool handleObjCPointerTypeAttr(TypeProcessingState &state, AttributeList &attr, QualType &type) { - // Right now, we have exactly one of these attributes: objc_gc. - assert(attr.getKind() == AttributeList::AT_objc_gc); - return handleObjCGCTypeAttr(state, attr, type); + if (attr.getKind() == AttributeList::AT_objc_gc) + return handleObjCGCTypeAttr(state, attr, type); + assert(attr.getKind() == AttributeList::AT_objc_lifetime); + return handleObjCLifetimeTypeAttr(state, attr, type); } /// Given that an objc_gc attribute was written somewhere on a @@ -447,7 +453,12 @@ distributeFunctionTypeAttrToInnermost(TypeProcessingState &state, return true; } - return handleFunctionTypeAttr(state, attr, declSpecType); + if (handleFunctionTypeAttr(state, attr, declSpecType)) { + spliceAttrOutOfList(attr, attrList); + return true; + } + + return false; } /// A function type attribute was written in the decl spec. Try to @@ -512,6 +523,11 @@ static void distributeTypeAttrsFromDeclarator(TypeProcessingState &state, distributeObjCPointerTypeAttrFromDeclarator(state, *attr, declSpecType); break; + case AttributeList::AT_ns_returns_retained: + if (!state.getSema().getLangOptions().ObjCAutoRefCount) + break; + // fallthrough + FUNCTION_TYPE_ATTRS_CASELIST: distributeFunctionTypeAttrFromDeclarator(state, *attr, declSpecType); break; @@ -1017,6 +1033,51 @@ QualType Sema::BuildParenType(QualType T) { return Context.getParenType(T); } +/// Given that we're building a pointer or reference to the given +static QualType inferARCLifetimeForPointee(Sema &S, QualType type, + SourceLocation loc, + bool isReference) { + // Bail out if retention is unrequired or already specified. + if (!type->isObjCLifetimeType() || + type.getObjCLifetime() != Qualifiers::OCL_None) + return type; + + Qualifiers::ObjCLifetime implicitLifetime = Qualifiers::OCL_None; + + // If the object type is const-qualified, we can safely use + // __unsafe_unretained. This is safe (because there are no read + // barriers), and it'll be safe to coerce anything but __weak* to + // the resulting type. + if (type.isConstQualified()) { + implicitLifetime = Qualifiers::OCL_ExplicitNone; + + // Otherwise, check whether the static type does not require + // retaining. This currently only triggers for Class (possibly + // protocol-qualifed, and arrays thereof). + } else if (type->isObjCARCImplicitlyUnretainedType()) { + implicitLifetime = Qualifiers::OCL_ExplicitNone; + + // If that failed, give an error and recover using __autoreleasing. + } else { + // These types can show up in private ivars in system headers, so + // we need this to not be an error in those cases. Instead we + // want to delay. + if (S.DelayedDiagnostics.shouldDelayDiagnostics()) { + S.DelayedDiagnostics.add( + sema::DelayedDiagnostic::makeForbiddenType(loc, + diag::err_arc_indirect_no_lifetime, type, isReference)); + } else { + S.Diag(loc, diag::err_arc_indirect_no_lifetime) << type << isReference; + } + implicitLifetime = Qualifiers::OCL_Autoreleasing; + } + assert(implicitLifetime && "didn't infer any lifetime!"); + + Qualifiers qs; + qs.addObjCLifetime(implicitLifetime); + return S.Context.getQualifiedType(type, qs); +} + /// \brief Build a pointer type. /// /// \param T The type to which we'll be building a pointer. @@ -1041,6 +1102,10 @@ QualType Sema::BuildPointerType(QualType T, assert(!T->isObjCObjectType() && "Should build ObjCObjectPointerType"); + // In ARC, it is forbidden to build pointers to unqualified pointers. + if (getLangOptions().ObjCAutoRefCount) + T = inferARCLifetimeForPointee(*this, T, Loc, /*reference*/ false); + // Build the pointer type. return Context.getPointerType(T); } @@ -1094,6 +1159,10 @@ QualType Sema::BuildReferenceType(QualType T, bool SpelledAsLValue, return QualType(); } + // In ARC, it is forbidden to build references to unqualified pointers. + if (getLangOptions().ObjCAutoRefCount) + T = inferARCLifetimeForPointee(*this, T, Loc, /*reference*/ true); + // Handle restrict on references. if (LValueRef) return Context.getLValueReferenceType(T, SpelledAsLValue); @@ -1266,10 +1335,12 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM, if (!getLangOptions().C99) { if (T->isVariableArrayType()) { // Prohibit the use of non-POD types in VLAs. + QualType BaseT = Context.getBaseElementType(T); if (!T->isDependentType() && - !Context.getBaseElementType(T)->isPODType()) { + !BaseT.isPODType(Context) && + !BaseT->isObjCLifetimeType()) { Diag(Loc, diag::err_vla_non_pod) - << Context.getBaseElementType(T); + << BaseT; return QualType(); } // Prohibit the use of VLAs during template argument deduction. @@ -1490,6 +1561,111 @@ QualType Sema::GetTypeFromParser(ParsedType Ty, TypeSourceInfo **TInfo) { return QT; } +/// Given that this is the declaration of a parameter under ARC, +/// attempt to infer attributes and such for pointer-to-whatever +/// types. +static void inferARCWriteback(TypeProcessingState &state, + QualType &declSpecType) { + Sema &S = state.getSema(); + Declarator &declarator = state.getDeclarator(); + + // TODO: should we care about decl qualifiers? + + // Check whether the declarator has the expected form. We walk + // from the inside out in order to make the block logic work. + unsigned outermostPointerIndex = 0; + bool isBlockPointer = false; + unsigned numPointers = 0; + for (unsigned i = 0, e = declarator.getNumTypeObjects(); i != e; ++i) { + unsigned chunkIndex = i; + DeclaratorChunk &chunk = declarator.getTypeObject(chunkIndex); + switch (chunk.Kind) { + case DeclaratorChunk::Paren: + // Ignore parens. + break; + + case DeclaratorChunk::Reference: + case DeclaratorChunk::Pointer: + // Count the number of pointers. Treat references + // interchangeably as pointers; if they're mis-ordered, normal + // type building will discover that. + outermostPointerIndex = chunkIndex; + numPointers++; + break; + + case DeclaratorChunk::BlockPointer: + // If we have a pointer to block pointer, that's an acceptable + // indirect reference; anything else is not an application of + // the rules. + if (numPointers != 1) return; + numPointers++; + outermostPointerIndex = chunkIndex; + isBlockPointer = true; + + // We don't care about pointer structure in return values here. + goto done; + + case DeclaratorChunk::Array: // suppress if written (id[])? + case DeclaratorChunk::Function: + case DeclaratorChunk::MemberPointer: + return; + } + } + done: + + // If we have *one* pointer, then we want to throw the qualifier on + // the declaration-specifiers, which means that it needs to be a + // retainable object type. + if (numPointers == 1) { + // If it's not a retainable object type, the rule doesn't apply. + if (!declSpecType->isObjCRetainableType()) return; + + // If it already has lifetime, don't do anything. + if (declSpecType.getObjCLifetime()) return; + + // Otherwise, modify the type in-place. + Qualifiers qs; + + if (declSpecType->isObjCARCImplicitlyUnretainedType()) + qs.addObjCLifetime(Qualifiers::OCL_ExplicitNone); + else + qs.addObjCLifetime(Qualifiers::OCL_Autoreleasing); + declSpecType = S.Context.getQualifiedType(declSpecType, qs); + + // If we have *two* pointers, then we want to throw the qualifier on + // the outermost pointer. + } else if (numPointers == 2) { + // If we don't have a block pointer, we need to check whether the + // declaration-specifiers gave us something that will turn into a + // retainable object pointer after we slap the first pointer on it. + if (!isBlockPointer && !declSpecType->isObjCObjectType()) + return; + + // Look for an explicit lifetime attribute there. + DeclaratorChunk &chunk = declarator.getTypeObject(outermostPointerIndex); + assert(chunk.Kind == DeclaratorChunk::Pointer || + chunk.Kind == DeclaratorChunk::BlockPointer); + for (const AttributeList *attr = chunk.getAttrs(); attr; + attr = attr->getNext()) + if (attr->getKind() == AttributeList::AT_objc_lifetime) + return; + + // If there wasn't one, add one (with an invalid source location + // so that we don't make an AttributedType for it). + AttributeList *attr = declarator.getAttributePool() + .create(&S.Context.Idents.get("objc_lifetime"), SourceLocation(), + /*scope*/ 0, SourceLocation(), + &S.Context.Idents.get("autoreleasing"), SourceLocation(), + /*args*/ 0, 0, + /*declspec*/ false, /*C++0x*/ false); + spliceAttrIntoList(*attr, chunk.getAttrListRef()); + + // Any other number of pointers/references does not trigger the rule. + } else return; + + // TODO: mark whether we did this inference? +} + static void DiagnoseIgnoredQualifiers(unsigned Quals, SourceLocation ConstQualLoc, SourceLocation VolatileQualLoc, @@ -1599,6 +1775,9 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, if (D.getAttributes()) distributeTypeAttrsFromDeclarator(state, T); + if (D.isPrototypeContext() && getLangOptions().ObjCAutoRefCount) + inferARCWriteback(state, T); + // C++0x [dcl.spec.auto]p5: reject 'auto' if it is not in an allowed context. // In C++0x, a function declarator using 'auto' must have a trailing return // type (this is checked later) and we can skip this. In other languages @@ -1922,6 +2101,10 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, llvm::SmallVector<QualType, 16> ArgTys; ArgTys.reserve(FTI.NumArgs); + llvm::SmallVector<bool, 16> ConsumedArguments; + ConsumedArguments.reserve(FTI.NumArgs); + bool HasAnyConsumedArguments = false; + for (unsigned i = 0, e = FTI.NumArgs; i != e; ++i) { ParmVarDecl *Param = cast<ParmVarDecl>(FTI.ArgInfo[i].Param); QualType ArgTy = Param->getType(); @@ -1967,9 +2150,18 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, } } + if (getLangOptions().ObjCAutoRefCount) { + bool Consumed = Param->hasAttr<NSConsumedAttr>(); + ConsumedArguments.push_back(Consumed); + HasAnyConsumedArguments |= Consumed; + } + ArgTys.push_back(ArgTy); } + if (HasAnyConsumedArguments) + EPI.ConsumedArguments = ConsumedArguments.data(); + llvm::SmallVector<QualType, 4> Exceptions; EPI.ExceptionSpecType = FTI.getExceptionSpecType(); if (FTI.getExceptionSpecType() == EST_Dynamic) { @@ -2282,6 +2474,8 @@ static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) { return AttributeList::AT_neon_polyvector_type; case AttributedType::attr_objc_gc: return AttributeList::AT_objc_gc; + case AttributedType::attr_objc_lifetime: + return AttributeList::AT_objc_lifetime; case AttributedType::attr_noreturn: return AttributeList::AT_noreturn; case AttributedType::attr_cdecl: @@ -2512,6 +2706,9 @@ namespace { llvm_unreachable("qualified type locs not expected here!"); } + void VisitAttributedTypeLoc(AttributedTypeLoc TL) { + fillAttributedTypeLoc(TL, Chunk.getAttrs()); + } void VisitBlockPointerTypeLoc(BlockPointerTypeLoc TL) { assert(Chunk.Kind == DeclaratorChunk::BlockPointer); TL.setCaretLoc(Chunk.Loc); @@ -2707,8 +2904,6 @@ TypeResult Sema::ActOnTypeName(Scope *S, Declarator &D) { return CreateParsedType(T, TInfo); } - - //===----------------------------------------------------------------------===// // Type Attribute Processing //===----------------------------------------------------------------------===// @@ -2767,6 +2962,83 @@ static void HandleAddressSpaceTypeAttribute(QualType &Type, Type = S.Context.getAddrSpaceQualType(Type, ASIdx); } +/// handleObjCLifetimeTypeAttr - Process an objc_lifetime +/// attribute on the specified type. +/// +/// Returns 'true' if the attribute was handled. +static bool handleObjCLifetimeTypeAttr(TypeProcessingState &state, + AttributeList &attr, + QualType &type) { + if (!type->isObjCRetainableType() && !type->isDependentType()) + return false; + + Sema &S = state.getSema(); + + if (type.getQualifiers().getObjCLifetime()) { + S.Diag(attr.getLoc(), diag::err_attr_objc_lifetime_redundant) + << type; + return true; + } + + if (!attr.getParameterName()) { + S.Diag(attr.getLoc(), diag::err_attribute_argument_n_not_string) + << "objc_lifetime" << 1; + attr.setInvalid(); + return true; + } + + Qualifiers::ObjCLifetime lifetime; + if (attr.getParameterName()->isStr("none")) + lifetime = Qualifiers::OCL_ExplicitNone; + else if (attr.getParameterName()->isStr("strong")) + lifetime = Qualifiers::OCL_Strong; + else if (attr.getParameterName()->isStr("weak")) + lifetime = Qualifiers::OCL_Weak; + else if (attr.getParameterName()->isStr("autoreleasing")) + lifetime = Qualifiers::OCL_Autoreleasing; + else { + S.Diag(attr.getLoc(), diag::warn_attribute_type_not_supported) + << "objc_lifetime" << attr.getParameterName(); + attr.setInvalid(); + return true; + } + + // Consume lifetime attributes without further comment outside of + // ARC mode. + if (!S.getLangOptions().ObjCAutoRefCount) + return true; + + Qualifiers qs; + qs.setObjCLifetime(lifetime); + QualType origType = type; + type = S.Context.getQualifiedType(type, qs); + + // If we have a valid source location for the attribute, use an + // AttributedType instead. + if (attr.getLoc().isValid()) + type = S.Context.getAttributedType(AttributedType::attr_objc_lifetime, + origType, type); + + // Forbid __weak if we don't have a runtime. + if (lifetime == Qualifiers::OCL_Weak && + S.getLangOptions().ObjCNoAutoRefCountRuntime) { + + // Actually, delay this until we know what we're parsing. + if (S.DelayedDiagnostics.shouldDelayDiagnostics()) { + S.DelayedDiagnostics.add( + sema::DelayedDiagnostic::makeForbiddenType(attr.getLoc(), + diag::err_arc_weak_no_runtime, type, /*ignored*/ 0)); + } else { + S.Diag(attr.getLoc(), diag::err_arc_weak_no_runtime); + } + + attr.setInvalid(); + return true; + } + + return true; +} + /// handleObjCGCTypeAttr - Process the __attribute__((objc_gc)) type /// attribute on the specified type. Returns true to indicate that /// the attribute was handled, false to indicate that the type does @@ -2977,6 +3249,23 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, return true; } + // ns_returns_retained is not always a type attribute, but if we got + // here, we're treating it as one right now. + if (attr.getKind() == AttributeList::AT_ns_returns_retained) { + assert(S.getLangOptions().ObjCAutoRefCount && + "ns_returns_retained treated as type attribute in non-ARC"); + if (attr.getNumArgs()) return true; + + // Delay if this is not a function type. + if (!unwrapped.isFunctionType()) + return false; + + FunctionType::ExtInfo EI + = unwrapped.get()->getExtInfo().withProducesResult(true); + type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI)); + return true; + } + if (attr.getKind() == AttributeList::AT_regparm) { unsigned value; if (S.CheckRegparmAttr(attr, value)) @@ -3288,11 +3577,15 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, VectorType::NeonPolyVector, "neon_polyvector_type"); break; - case AttributeList::AT_opencl_image_access: HandleOpenCLImageAccessAttribute(type, attr, state.getSema()); break; + case AttributeList::AT_ns_returns_retained: + if (!state.getSema().getLangOptions().ObjCAutoRefCount) + break; + // fallthrough into the function attrs + FUNCTION_TYPE_ATTRS_CASELIST: // Never process function type attributes as part of the // declaration-specifiers. diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index ff2e46a9026..ba4ee3426a4 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -1192,6 +1192,16 @@ public: Body); } + /// \brief Build a new Objective-C @autoreleasepool statement. + /// + /// By default, performs semantic analysis to build the new statement. + /// Subclasses may override this routine to provide different behavior. + StmtResult RebuildObjCAutoreleasePoolStmt(SourceLocation AtLoc, + Stmt *Body) { + return getSema().ActOnObjCAutoreleasePoolStmt(AtLoc, Body); + } + + /// \brief Build a new Objective-C fast enumeration statement. /// /// By default, performs semantic analysis to build the new statement. @@ -3162,6 +3172,13 @@ TreeTransform<Derived>::TransformQualifiedType(TypeLocBuilder &TLB, if (Result->isFunctionType() || Result->isReferenceType()) return Result; + // Suppress Objective-C lifetime qualifiers if they don't make sense for the + // resulting type or if the resulting type already has one. + if (Quals.hasObjCLifetime() && + (Result.getObjCLifetime() || + (!Result->isObjCLifetimeType() && !Result->isDependentType()))) + Quals.removeObjCLifetime(); + if (!Quals.empty()) { Result = SemaRef.BuildQualifiedType(Result, T.getBeginLoc(), Quals); TLB.push<QualifiedTypeLoc>(Result); @@ -3333,7 +3350,11 @@ QualType TreeTransform<Derived>::TransformPointerType(TypeLocBuilder &TLB, if (Result.isNull()) return QualType(); } - + + // Objective-C ARC can add lifetime qualifiers to the type that we're + // pointing to. + TLB.TypeWasModifiedSafely(Result->getPointeeType()); + PointerTypeLoc NewT = TLB.push<PointerTypeLoc>(Result); NewT.setSigilLoc(TL.getSigilLoc()); return Result; @@ -3387,6 +3408,11 @@ TreeTransform<Derived>::TransformReferenceType(TypeLocBuilder &TLB, return QualType(); } + // Objective-C ARC can add lifetime qualifiers to the type that we're + // referring to. + TLB.TypeWasModifiedSafely( + Result->getAs<ReferenceType>()->getPointeeTypeAsWritten()); + // r-value references can be rebuilt as l-value references. ReferenceTypeLoc NewTL; if (isa<LValueReferenceType>(Result)) @@ -5435,6 +5461,25 @@ TreeTransform<Derived>::TransformObjCAtSynchronizedStmt( template<typename Derived> StmtResult +TreeTransform<Derived>::TransformObjCAutoreleasePoolStmt( + ObjCAutoreleasePoolStmt *S) { + // Transform the body. + StmtResult Body = getDerived().TransformStmt(S->getSubStmt()); + if (Body.isInvalid()) + return StmtError(); + + // If nothing changed, just retain this statement. + if (!getDerived().AlwaysRebuild() && + Body.get() == S->getSubStmt()) + return SemaRef.Owned(S); + + // Build a new statement. + return getDerived().RebuildObjCAutoreleasePoolStmt( + S->getAtLoc(), Body.get()); +} + +template<typename Derived> +StmtResult TreeTransform<Derived>::TransformObjCForCollectionStmt( ObjCForCollectionStmt *S) { // Transform the element statement. @@ -7609,6 +7654,43 @@ TreeTransform<Derived>::TransformObjCEncodeExpr(ObjCEncodeExpr *E) { } template<typename Derived> +ExprResult TreeTransform<Derived>:: +TransformObjCIndirectCopyRestoreExpr(ObjCIndirectCopyRestoreExpr *E) { + ExprResult result = getDerived().TransformExpr(E->getSubExpr()); + if (result.isInvalid()) return ExprError(); + Expr *subExpr = result.take(); + + if (!getDerived().AlwaysRebuild() && + subExpr == E->getSubExpr()) + return SemaRef.Owned(E); + + return SemaRef.Owned(new(SemaRef.Context) + ObjCIndirectCopyRestoreExpr(subExpr, E->getType(), E->shouldCopy())); +} + +template<typename Derived> +ExprResult TreeTransform<Derived>:: +TransformObjCBridgedCastExpr(ObjCBridgedCastExpr *E) { + TypeSourceInfo *TSInfo + = getDerived().TransformType(E->getTypeInfoAsWritten()); + if (!TSInfo) + return ExprError(); + + ExprResult Result = getDerived().TransformExpr(E->getSubExpr()); + if (Result.isInvalid()) + return ExprError(); + + if (!getDerived().AlwaysRebuild() && + TSInfo == E->getTypeInfoAsWritten() && + Result.get() == E->getSubExpr()) + return SemaRef.Owned(E); + + return SemaRef.BuildObjCBridgedCast(E->getLParenLoc(), E->getBridgeKind(), + E->getBridgeKeywordLoc(), TSInfo, + Result.get()); +} + +template<typename Derived> ExprResult TreeTransform<Derived>::TransformObjCMessageExpr(ObjCMessageExpr *E) { // Transform arguments. diff --git a/clang/lib/Sema/TypeLocBuilder.h b/clang/lib/Sema/TypeLocBuilder.h index 3570737d11b..f0944f1205b 100644 --- a/clang/lib/Sema/TypeLocBuilder.h +++ b/clang/lib/Sema/TypeLocBuilder.h @@ -87,6 +87,15 @@ class TypeLocBuilder { Index = Capacity; } + /// \brief Tell the TypeLocBuilder that the type it is storing has been + /// modified in some safe way that doesn't affect type-location information. + void TypeWasModifiedSafely(QualType T) { +#ifndef NDEBUG + assert(T.getLocalUnqualifiedType() == LastTy.getLocalUnqualifiedType()); + LastTy = T; +#endif + } + /// Pushes space for a new TypeLoc of the given type. Invalidates /// any TypeLocs previously retrieved from this builder. template <class TyLocType> TyLocType push(QualType T) { diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 8fb20d22b7d..1ee54b8f026 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -147,9 +147,11 @@ PCHValidator::ReadLanguageOptions(const LangOptions &LangOpts) { PARSE_LANGOPT_IMPORTANT(OpenCL, diag::warn_pch_opencl); PARSE_LANGOPT_IMPORTANT(CUDA, diag::warn_pch_cuda); PARSE_LANGOPT_BENIGN(CatchUndefined); + PARSE_LANGOPT_BENIGN(DefaultFPContract); PARSE_LANGOPT_IMPORTANT(ElideConstructors, diag::warn_pch_elide_constructors); PARSE_LANGOPT_BENIGN(SpellChecking); - PARSE_LANGOPT_BENIGN(DefaultFPContract); + PARSE_LANGOPT_IMPORTANT(ObjCAutoRefCount, diag::warn_pch_auto_ref_count); + PARSE_LANGOPT_BENIGN(ObjCInferRelatedReturnType); #undef PARSE_LANGOPT_IMPORTANT #undef PARSE_LANGOPT_BENIGN @@ -2980,6 +2982,7 @@ bool ASTReader::ParseLanguageOptions( PARSE_LANGOPT(ElideConstructors); PARSE_LANGOPT(SpellChecking); PARSE_LANGOPT(MRTD); + PARSE_LANGOPT(ObjCAutoRefCount); #undef PARSE_LANGOPT return Listener->ReadLanguageOptions(LangOpts); @@ -3225,12 +3228,13 @@ QualType ASTReader::ReadTypeRecord(unsigned Index) { } case TYPE_FUNCTION_NO_PROTO: { - if (Record.size() != 5) { + if (Record.size() != 6) { Error("incorrect encoding of no-proto function type"); return QualType(); } QualType ResultType = GetType(Record[0]); - FunctionType::ExtInfo Info(Record[1], Record[2], Record[3], (CallingConv)Record[4]); + FunctionType::ExtInfo Info(Record[1], Record[2], Record[3], + (CallingConv)Record[4], Record[5]); return Context->getFunctionNoProtoType(ResultType, Info); } @@ -3241,9 +3245,10 @@ QualType ASTReader::ReadTypeRecord(unsigned Index) { EPI.ExtInfo = FunctionType::ExtInfo(/*noreturn*/ Record[1], /*hasregparm*/ Record[2], /*regparm*/ Record[3], - static_cast<CallingConv>(Record[4])); + static_cast<CallingConv>(Record[4]), + /*produces*/ Record[5]); - unsigned Idx = 5; + unsigned Idx = 6; unsigned NumParams = Record[Idx++]; llvm::SmallVector<QualType, 16> ParamTypes; for (unsigned I = 0; I != NumParams; ++I) diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index f3f67a76c4d..0af3546223c 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -131,6 +131,8 @@ namespace clang { void VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E); void VisitObjCMessageExpr(ObjCMessageExpr *E); void VisitObjCIsaExpr(ObjCIsaExpr *E); + void VisitObjCIndirectCopyRestoreExpr(ObjCIndirectCopyRestoreExpr *E); + void VisitObjCBridgedCastExpr(ObjCBridgedCastExpr *E); void VisitObjCForCollectionStmt(ObjCForCollectionStmt *); void VisitObjCAtCatchStmt(ObjCAtCatchStmt *); @@ -138,6 +140,7 @@ namespace clang { void VisitObjCAtTryStmt(ObjCAtTryStmt *); void VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *); void VisitObjCAtThrowStmt(ObjCAtThrowStmt *); + void VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *); // C++ Statements void VisitCXXCatchStmt(CXXCatchStmt *S); @@ -602,6 +605,20 @@ void ASTStmtReader::VisitObjCIsaExpr(ObjCIsaExpr *E) { E->setArrow(Record[Idx++]); } +void ASTStmtReader:: +VisitObjCIndirectCopyRestoreExpr(ObjCIndirectCopyRestoreExpr *E) { + VisitExpr(E); + E->Operand = Reader.ReadSubExpr(); + E->setShouldCopy(Record[Idx++]); +} + +void ASTStmtReader::VisitObjCBridgedCastExpr(ObjCBridgedCastExpr *E) { + VisitExplicitCastExpr(E); + E->LParenLoc = ReadSourceLocation(Record, Idx); + E->BridgeKeywordLoc = ReadSourceLocation(Record, Idx); + E->Kind = Record[Idx++]; +} + void ASTStmtReader::VisitCastExpr(CastExpr *E) { VisitExpr(E); unsigned NumBaseSpecs = Record[Idx++]; @@ -929,6 +946,7 @@ void ASTStmtReader::VisitObjCMessageExpr(ObjCMessageExpr *E) { VisitExpr(E); assert(Record[Idx] == E->getNumArgs()); ++Idx; + E->setDelegateInitCall(Record[Idx++]); ObjCMessageExpr::ReceiverKind Kind = static_cast<ObjCMessageExpr::ReceiverKind>(Record[Idx++]); switch (Kind) { @@ -987,6 +1005,12 @@ void ASTStmtReader::VisitObjCAtFinallyStmt(ObjCAtFinallyStmt *S) { S->setAtFinallyLoc(ReadSourceLocation(Record, Idx)); } +void ASTStmtReader::VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { + VisitStmt(S); + S->setSubStmt(Reader.ReadSubStmt()); + S->setAtLoc(ReadSourceLocation(Record, Idx)); +} + void ASTStmtReader::VisitObjCAtTryStmt(ObjCAtTryStmt *S) { VisitStmt(S); assert(Record[Idx] == S->getNumCatchStmts()); @@ -1784,6 +1808,12 @@ Stmt *ASTReader::ReadStmtFromStream(PerFileData &F) { case EXPR_OBJC_ISA: S = new (Context) ObjCIsaExpr(Empty); break; + case EXPR_OBJC_INDIRECT_COPY_RESTORE: + S = new (Context) ObjCIndirectCopyRestoreExpr(Empty); + break; + case EXPR_OBJC_BRIDGED_CAST: + S = new (Context) ObjCBridgedCastExpr(Empty); + break; case STMT_OBJC_FOR_COLLECTION: S = new (Context) ObjCForCollectionStmt(Empty); break; @@ -1804,7 +1834,9 @@ Stmt *ASTReader::ReadStmtFromStream(PerFileData &F) { case STMT_OBJC_AT_THROW: S = new (Context) ObjCAtThrowStmt(Empty); break; - + case STMT_OBJC_AUTORELEASE_POOL: + S = new (Context) ObjCAutoreleasePoolStmt(Empty); + break; case STMT_CXX_CATCH: S = new (Context) CXXCatchStmt(Empty); break; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index ba9032e0d33..80e790c7968 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -170,6 +170,7 @@ void ASTTypeWriter::VisitFunctionType(const FunctionType *T) { Record.push_back(C.getRegParm()); // FIXME: need to stabilize encoding of calling convention... Record.push_back(C.getCC()); + Record.push_back(C.getProducesResult()); } void ASTTypeWriter::VisitFunctionNoProtoType(const FunctionNoProtoType *T) { @@ -1109,6 +1110,8 @@ void ASTWriter::WriteLanguageOptions(const LangOptions &LangOpts) { Record.push_back(LangOpts.ElideConstructors); Record.push_back(LangOpts.SpellChecking); Record.push_back(LangOpts.MRTD); + Record.push_back(LangOpts.ObjCAutoRefCount); + Record.push_back(LangOpts.ObjCInferRelatedReturnType); Stream.EmitRecord(LANGUAGE_OPTIONS, Record); } diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 00e24049464..c6d182d4254 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -105,6 +105,8 @@ namespace clang { void VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E); void VisitObjCMessageExpr(ObjCMessageExpr *E); void VisitObjCIsaExpr(ObjCIsaExpr *E); + void VisitObjCIndirectCopyRestoreExpr(ObjCIndirectCopyRestoreExpr *E); + void VisitObjCBridgedCastExpr(ObjCBridgedCastExpr *E); // Objective-C Statements void VisitObjCForCollectionStmt(ObjCForCollectionStmt *); @@ -113,6 +115,7 @@ namespace clang { void VisitObjCAtTryStmt(ObjCAtTryStmt *); void VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *); void VisitObjCAtThrowStmt(ObjCAtThrowStmt *); + void VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *); // C++ Statements void VisitCXXCatchStmt(CXXCatchStmt *S); @@ -606,6 +609,22 @@ void ASTStmtWriter::VisitObjCIsaExpr(ObjCIsaExpr *E) { Code = serialization::EXPR_OBJC_ISA; } +void ASTStmtWriter:: +VisitObjCIndirectCopyRestoreExpr(ObjCIndirectCopyRestoreExpr *E) { + VisitExpr(E); + Writer.AddStmt(E->getSubExpr()); + Record.push_back(E->shouldCopy()); + Code = serialization::EXPR_OBJC_INDIRECT_COPY_RESTORE; +} + +void ASTStmtWriter::VisitObjCBridgedCastExpr(ObjCBridgedCastExpr *E) { + VisitExplicitCastExpr(E); + Writer.AddSourceLocation(E->getLParenLoc(), Record); + Writer.AddSourceLocation(E->getBridgeKeywordLoc(), Record); + Record.push_back(E->getBridgeKind()); // FIXME: Stable encoding + Code = serialization::EXPR_OBJC_BRIDGED_CAST; +} + void ASTStmtWriter::VisitCastExpr(CastExpr *E) { VisitExpr(E); Record.push_back(E->path_size()); @@ -914,6 +933,7 @@ void ASTStmtWriter::VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { void ASTStmtWriter::VisitObjCMessageExpr(ObjCMessageExpr *E) { VisitExpr(E); Record.push_back(E->getNumArgs()); + Record.push_back(E->isDelegateInitCall()); Record.push_back((unsigned)E->getReceiverKind()); // FIXME: stable encoding switch (E->getReceiverKind()) { case ObjCMessageExpr::Instance: @@ -973,6 +993,12 @@ void ASTStmtWriter::VisitObjCAtFinallyStmt(ObjCAtFinallyStmt *S) { Code = serialization::STMT_OBJC_FINALLY; } +void ASTStmtWriter::VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { + Writer.AddStmt(S->getSubStmt()); + Writer.AddSourceLocation(S->getAtLoc(), Record); + Code = serialization::STMT_OBJC_AUTORELEASE_POOL; +} + void ASTStmtWriter::VisitObjCAtTryStmt(ObjCAtTryStmt *S) { Record.push_back(S->getNumCatchStmts()); Record.push_back(S->getFinallyStmt() != 0); diff --git a/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp index bc1d823dde0..ec2a88a5568 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -254,13 +254,13 @@ public: return; if (Expr* E = V->getInit()) { + while (ExprWithCleanups *exprClean = dyn_cast<ExprWithCleanups>(E)) + E = exprClean->getSubExpr(); + // Don't warn on C++ objects (yet) until we can show that their // constructors/destructors don't have side effects. if (isa<CXXConstructExpr>(E)) return; - - if (isa<ExprWithCleanups>(E)) - return; // A dead initialization is a variable that is dead after it // is initialized. We don't flag warnings for those variables diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 07de8706df2..73ce359edc3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -121,6 +121,11 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, return; if (R->hasStackStorage()) { + // Automatic reference counting automatically copies blocks. + if (C.getASTContext().getLangOptions().ObjCAutoRefCount && + isa<BlockDataRegion>(R)) + return; + EmitStackError(C, R, RetE); return; } @@ -135,12 +140,13 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B, // a memory region in the stack space. class CallBack : public StoreManager::BindingsHandler { private: + ExprEngine &Eng; const StackFrameContext *CurSFC; public: llvm::SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V; - CallBack(const LocationContext *LCtx) - : CurSFC(LCtx->getCurrentStackFrame()) {} + CallBack(ExprEngine &Eng, const LocationContext *LCtx) + : Eng(Eng), CurSFC(LCtx->getCurrentStackFrame()) {} bool HandleBinding(StoreManager &SMgr, Store store, const MemRegion *region, SVal val) { @@ -151,7 +157,13 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B, const MemRegion *vR = val.getAsRegion(); if (!vR) return true; - + + // Under automated retain release, it is okay to assign a block + // directly to a global variable. + if (Eng.getContext().getLangOptions().ObjCAutoRefCount && + isa<BlockDataRegion>(vR)) + return true; + if (const StackSpaceRegion *SSR = dyn_cast<StackSpaceRegion>(vR->getMemorySpace())) { // If the global variable holds a location in the current stack frame, @@ -164,7 +176,7 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B, } }; - CallBack cb(B.getPredecessor()->getLocationContext()); + CallBack cb(Eng, B.getPredecessor()->getLocationContext()); state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb); if (cb.V.empty()) diff --git a/clang/lib/StaticAnalyzer/Core/CFRefCount.cpp b/clang/lib/StaticAnalyzer/Core/CFRefCount.cpp index 0512e2f08d0..33118fa901d 100644 --- a/clang/lib/StaticAnalyzer/Core/CFRefCount.cpp +++ b/clang/lib/StaticAnalyzer/Core/CFRefCount.cpp @@ -126,6 +126,7 @@ public: /// ArgEffect is used to summarize a function/method call's effect on a /// particular argument. enum ArgEffect { Autorelease, Dealloc, DecRef, DecRefMsg, DoNothing, + DecRefBridgedTransfered, DoNothingByRef, IncRefMsg, IncRef, MakeCollectable, MayEscape, NewAutoreleasePool, SelfOwn, StopTracking }; @@ -148,7 +149,8 @@ namespace { class RetEffect { public: enum Kind { NoRet, Alias, OwnedSymbol, OwnedAllocatedSymbol, - NotOwnedSymbol, GCNotOwnedSymbol, ReceiverAlias, + NotOwnedSymbol, GCNotOwnedSymbol, ARCNotOwnedSymbol, + ReceiverAlias, OwnedWhenTrackedReceiver }; enum ObjKind { CF, ObjC, AnyObj }; @@ -195,7 +197,9 @@ public: static RetEffect MakeGCNotOwned() { return RetEffect(GCNotOwnedSymbol, ObjC); } - + static RetEffect MakeARCNotOwned() { + return RetEffect(ARCNotOwnedSymbol, ObjC); + } static RetEffect MakeNoRet() { return RetEffect(NoRet); } @@ -636,6 +640,9 @@ class RetainSummaryManager { /// GCEnabled - Records whether or not the analyzed code runs in GC mode. const bool GCEnabled; + /// Records whether or not the analyzed code runs in ARC mode. + const bool ARCEnabled; + /// FuncSummaries - A map from FunctionDecls to summaries. FuncSummariesTy FuncSummaries; @@ -788,14 +795,20 @@ private: public: - RetainSummaryManager(ASTContext& ctx, bool gcenabled) + RetainSummaryManager(ASTContext& ctx, bool gcenabled, bool usesARC) : Ctx(ctx), CFDictionaryCreateII(&ctx.Idents.get("CFDictionaryCreate")), - GCEnabled(gcenabled), AF(BPAlloc), ScratchArgs(AF.getEmptyMap()), - ObjCAllocRetE(gcenabled ? RetEffect::MakeGCNotOwned() - : RetEffect::MakeOwned(RetEffect::ObjC, true)), - ObjCInitRetE(gcenabled ? RetEffect::MakeGCNotOwned() - : RetEffect::MakeOwnedWhenTrackedReceiver()), + GCEnabled(gcenabled), + ARCEnabled(usesARC), + AF(BPAlloc), ScratchArgs(AF.getEmptyMap()), + ObjCAllocRetE(gcenabled + ? RetEffect::MakeGCNotOwned() + : (usesARC ? RetEffect::MakeARCNotOwned() + : RetEffect::MakeOwned(RetEffect::ObjC, true))), + ObjCInitRetE(gcenabled + ? RetEffect::MakeGCNotOwned() + : (usesARC ? RetEffect::MakeARCNotOwned() + : RetEffect::MakeOwnedWhenTrackedReceiver())), DefaultSummary(AF.getEmptyMap() /* per-argument effects (none) */, RetEffect::MakeNoRet() /* return effect */, MayEscape, /* default argument effect */ @@ -871,6 +884,10 @@ public: bool isGCEnabled() const { return GCEnabled; } + bool isARCEnabled() const { return ARCEnabled; } + + bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; } + RetainSummary *copySummary(RetainSummary *OldSumm) { RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>(); new (Summ) RetainSummary(*OldSumm); @@ -1654,7 +1671,6 @@ public: const char* nl, const char* sep); }; -private: typedef llvm::DenseMap<const ExplodedNode*, const RetainSummary*> SummaryLogTy; @@ -1691,7 +1707,7 @@ private: public: CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts) - : Summaries(Ctx, gcenabled), + : Summaries(Ctx, gcenabled, (bool)lopts.ObjCAutoRefCount), LOpts(lopts), useAfterRelease(0), releaseNotOwned(0), deallocGC(0), deallocNotOwned(0), leakWithinFunction(0), leakAtReturn(0), overAutorelease(0), @@ -1706,6 +1722,8 @@ public: } bool isGCEnabled() const { return Summaries.isGCEnabled(); } + bool isARCorGCEnabled() const { return Summaries.isARCorGCEnabled(); } + const LangOptions& getLangOptions() const { return LOpts; } const RetainSummary *getSummaryOfNode(const ExplodedNode *N) const { @@ -2777,6 +2795,7 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst, } case RetEffect::GCNotOwnedSymbol: + case RetEffect::ARCNotOwnedSymbol: case RetEffect::NotOwnedSymbol: { unsigned Count = Builder.getCurrentBlockCount(); SValBuilder &svalBuilder = Eng.getSValBuilder(); @@ -3103,8 +3122,8 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym, // In GC mode [... release] and [... retain] do nothing. switch (E) { default: break; - case IncRefMsg: E = isGCEnabled() ? DoNothing : IncRef; break; - case DecRefMsg: E = isGCEnabled() ? DoNothing : DecRef; break; + case IncRefMsg: E = isARCorGCEnabled() ? DoNothing : IncRef; break; + case DecRefMsg: E = isARCorGCEnabled() ? DoNothing : DecRef; break; case MakeCollectable: E = isGCEnabled() ? DecRef : DoNothing; break; case NewAutoreleasePool: E = isGCEnabled() ? DoNothing : NewAutoreleasePool; break; @@ -3118,9 +3137,13 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym, } switch (E) { - default: - assert (false && "Unhandled CFRef transition."); - + case DecRefMsg: + case IncRefMsg: + case MakeCollectable: + assert(false && + "DecRefMsg/IncRefMsg/MakeCollectable already transformed"); + return state; + case Dealloc: // Any use of -dealloc in GC is *bad*. if (isGCEnabled()) { @@ -3193,6 +3216,7 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym, V = V ^ RefVal::NotOwned; // Fall-through. case DecRef: + case DecRefBridgedTransfered: switch (V.getKind()) { default: // case 'RefVal::Released' handled above. @@ -3200,7 +3224,9 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym, case RefVal::Owned: assert(V.getCount() > 0); - if (V.getCount() == 1) V = V ^ RefVal::Released; + if (V.getCount() == 1) + V = V ^ (E == DecRefBridgedTransfered ? + RefVal::NotOwned : RefVal::Released); V = V - 1; break; @@ -3468,7 +3494,9 @@ void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst, namespace { class RetainReleaseChecker - : public Checker< check::PostStmt<BlockExpr>, check::RegionChanges > { + : public Checker< check::PostStmt<BlockExpr>, + check::PostStmt<CastExpr>, + check::RegionChanges > { public: bool wantsRegionUpdate; @@ -3476,6 +3504,9 @@ public: void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; + + void checkPostStmt(const CastExpr *CE, CheckerContext &C) const; + const GRState *checkRegionChanges(const GRState *state, const StoreManager::InvalidatedSymbols *invalidated, const MemRegion * const *begin, @@ -3545,6 +3576,48 @@ void RetainReleaseChecker::checkPostStmt(const BlockExpr *BE, C.addTransition(state); } +void RetainReleaseChecker::checkPostStmt(const CastExpr *CE, + CheckerContext &C) const { + const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE); + if (!BE) + return; + + ArgEffect AE; + + switch (BE->getBridgeKind()) { + case clang::OBC_Bridge: + // Do nothing. + return; + case clang::OBC_BridgeRetained: + AE = IncRef; + break; + case clang::OBC_BridgeTransfer: + AE = DecRefBridgedTransfered; + break; + } + + const GRState *state = C.getState(); + SymbolRef Sym = state->getSVal(CE).getAsLocSymbol(); + if (!Sym) + return; + const RefVal* T = state->get<RefBindings>(Sym); + if (!T) + return; + + // This is gross. Once the checker and CFRefCount are unified, + // this will go away. + CFRefCount &cf = static_cast<CFRefCount&>(C.getEngine().getTF()); + RefVal::Kind hasErr = (RefVal::Kind) 0; + state = cf.Update(state, Sym, *T, AE, hasErr); + + if (hasErr) { + + return; + } + + C.generateNode(state); +} + //===----------------------------------------------------------------------===// // Transfer function creation for external clients. //===----------------------------------------------------------------------===// diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index aed39eb0cec..21efbac6996 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -442,7 +442,8 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, } switch (S->getStmtClass()) { - // C++ stuff we don't support yet. + // C++ and ARC stuff we don't support yet. + case Expr::ObjCIndirectCopyRestoreExprClass: case Stmt::CXXBindTemporaryExprClass: case Stmt::CXXCatchStmtClass: case Stmt::CXXDependentScopeMemberExprClass: @@ -520,14 +521,27 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, VisitObjCPropertyRefExpr(cast<ObjCPropertyRefExpr>(S), Pred, Dst); break; + case Stmt::ImplicitValueInitExprClass: { + const GRState *state = GetState(Pred); + QualType ty = cast<ImplicitValueInitExpr>(S)->getType(); + SVal val = svalBuilder.makeZeroVal(ty); + MakeNode(Dst, S, Pred, state->BindExpr(S, val)); + break; + } + + case Stmt::ExprWithCleanupsClass: { + Visit(cast<ExprWithCleanups>(S)->getSubExpr(), Pred, Dst); + break; + } + // Cases not handled yet; but will handle some day. case Stmt::DesignatedInitExprClass: case Stmt::ExtVectorElementExprClass: case Stmt::ImaginaryLiteralClass: - case Stmt::ImplicitValueInitExprClass: case Stmt::ObjCAtCatchStmtClass: case Stmt::ObjCAtFinallyStmtClass: case Stmt::ObjCAtTryStmtClass: + case Stmt::ObjCAutoreleasePoolStmtClass: case Stmt::ObjCEncodeExprClass: case Stmt::ObjCIsaExprClass: case Stmt::ObjCProtocolExprClass: @@ -548,7 +562,6 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, case Stmt::IntegerLiteralClass: case Stmt::CharacterLiteralClass: case Stmt::CXXBoolLiteralExprClass: - case Stmt::ExprWithCleanupsClass: case Stmt::FloatingLiteralClass: case Stmt::SizeOfPackExprClass: case Stmt::CXXNullPtrLiteralExprClass: @@ -668,9 +681,22 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, case Stmt::CXXDynamicCastExprClass: case Stmt::CXXReinterpretCastExprClass: case Stmt::CXXConstCastExprClass: - case Stmt::CXXFunctionalCastExprClass: { + case Stmt::CXXFunctionalCastExprClass: + case Stmt::ObjCBridgedCastExprClass: { const CastExpr* C = cast<CastExpr>(S); - VisitCast(C, C->getSubExpr(), Pred, Dst); + // Handle the previsit checks. + ExplodedNodeSet dstPrevisit; + getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, C, *this); + + // Handle the expression itself. + ExplodedNodeSet dstExpr; + for (ExplodedNodeSet::iterator i = dstPrevisit.begin(), + e = dstPrevisit.end(); i != e ; ++i) { + VisitCast(C, C->getSubExpr(), *i, dstExpr); + } + + // Handle the postvisit checks. + getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, C, *this); break; } @@ -2148,10 +2174,18 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, Pred = *I; switch (CastE->getCastKind()) { + case CK_LValueToRValue: + assert(false && "LValueToRValue casts handled earlier."); + case CK_GetObjCProperty: + assert(false && "GetObjCProperty casts handled earlier."); case CK_ToVoid: Dst.Add(Pred); continue; - case CK_LValueToRValue: + // The analyzer doesn't do anything special with these casts, + // since it understands retain/release semantics already. + case CK_ObjCProduceObject: + case CK_ObjCConsumeObject: // Fall-through. + // True no-ops. case CK_NoOp: case CK_FunctionToPointerDecay: { // Copy the SVal of Ex to CastE. @@ -2161,7 +2195,6 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, MakeNode(Dst, CastE, Pred, state); continue; } - case CK_GetObjCProperty: case CK_Dependent: case CK_ArrayToPointerDecay: case CK_BitCast: |