From f85e193739c953358c865005855253af4f68a497 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 15 Jun 2011 23:02:42 +0000 Subject: [PATCH] Automatic Reference Counting. Language-design credit goes to a lot of people, but I particularly want to single out Blaine Garst and Patrick Beard for their contributions. Compiler implementation credit goes to Argyrios, Doug, Fariborz, and myself, in no particular order. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@133103 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/LanguageExtensions.html | 7 + include/clang/AST/ASTContext.h | 16 +- include/clang/AST/CanonicalType.h | 2 +- include/clang/AST/DeclObjC.h | 19 +- include/clang/AST/Expr.h | 2 + include/clang/AST/ExprObjC.h | 130 +- include/clang/AST/OperationKinds.h | 25 +- include/clang/AST/ParentMap.h | 1 + include/clang/AST/PrettyPrinter.h | 6 +- include/clang/AST/RecursiveASTVisitor.h | 5 + include/clang/AST/Stmt.h | 8 + include/clang/AST/StmtObjC.h | 33 + include/clang/AST/Type.h | 238 ++- include/clang/Basic/Attr.td | 5 + include/clang/Basic/DiagnosticCommonKinds.td | 1 + include/clang/Basic/DiagnosticDriverKinds.td | 4 + .../clang/Basic/DiagnosticFrontendKinds.td | 3 + include/clang/Basic/DiagnosticGroups.td | 2 + include/clang/Basic/DiagnosticSemaKinds.td | 188 +- include/clang/Basic/LangOptions.h | 6 + include/clang/Basic/StmtNodes.td | 5 + include/clang/Basic/TokenKinds.def | 6 + include/clang/Driver/CC1Options.td | 15 + include/clang/Driver/Options.td | 15 + include/clang/Driver/ToolChain.h | 6 +- include/clang/Frontend/CodeGenOptions.h | 2 + include/clang/Frontend/FrontendOptions.h | 8 + include/clang/Frontend/PreprocessorOptions.h | 17 +- include/clang/Frontend/Utils.h | 1 + include/clang/Parse/Parser.h | 4 +- include/clang/Rewrite/Rewriter.h | 5 +- include/clang/Sema/AttributeList.h | 3 + include/clang/Sema/DeclSpec.h | 16 +- include/clang/Sema/DelayedDiagnostic.h | 41 +- include/clang/Sema/Initialization.h | 101 +- include/clang/Sema/Overload.h | 18 +- include/clang/Sema/Sema.h | 128 +- include/clang/Serialization/ASTBitCodes.h | 12 +- lib/AST/ASTContext.cpp | 52 +- lib/AST/DeclCXX.cpp | 36 +- lib/AST/DeclObjC.cpp | 24 +- lib/AST/DeclPrinter.cpp | 5 + lib/AST/Expr.cpp | 36 +- lib/AST/ExprClassification.cpp | 2 + lib/AST/ExprConstant.cpp | 19 +- lib/AST/ItaniumMangle.cpp | 42 +- lib/AST/ParentMap.cpp | 9 + lib/AST/StmtPrinter.cpp | 17 + lib/AST/StmtProfile.cpp | 15 + lib/AST/Type.cpp | 407 ++++- lib/AST/TypePrinter.cpp | 117 +- lib/Analysis/ReachableCode.cpp | 6 + lib/Basic/DiagnosticIDs.cpp | 5 + lib/Basic/IdentifierTable.cpp | 7 +- lib/Basic/Targets.cpp | 36 +- lib/CodeGen/CGBlocks.cpp | 224 ++- lib/CodeGen/CGBlocks.h | 2 +- lib/CodeGen/CGCall.cpp | 274 ++- lib/CodeGen/CGCall.h | 47 +- lib/CodeGen/CGClass.cpp | 93 +- lib/CodeGen/CGDebugInfo.cpp | 1 + lib/CodeGen/CGDecl.cpp | 256 ++- lib/CodeGen/CGDeclCXX.cpp | 27 +- lib/CodeGen/CGException.cpp | 6 +- lib/CodeGen/CGExpr.cpp | 216 ++- lib/CodeGen/CGExprAgg.cpp | 24 +- lib/CodeGen/CGExprCXX.cpp | 90 +- lib/CodeGen/CGExprConstant.cpp | 2 + lib/CodeGen/CGExprScalar.cpp | 57 +- lib/CodeGen/CGObjC.cpp | 1379 ++++++++++++++- lib/CodeGen/CGObjCGNU.cpp | 4 +- lib/CodeGen/CGObjCMac.cpp | 129 +- lib/CodeGen/CGObjCRuntime.h | 8 +- lib/CodeGen/CGStmt.cpp | 5 +- lib/CodeGen/CGValue.h | 28 +- lib/CodeGen/CodeGenFunction.cpp | 19 +- lib/CodeGen/CodeGenFunction.h | 83 +- lib/CodeGen/CodeGenModule.cpp | 21 +- lib/CodeGen/CodeGenModule.h | 87 +- lib/CodeGen/ItaniumCXXABI.cpp | 35 +- lib/CodeGen/TargetInfo.cpp | 6 +- lib/CodeGen/TargetInfo.h | 14 + lib/Driver/ToolChain.cpp | 9 +- lib/Driver/ToolChains.cpp | 97 +- lib/Driver/ToolChains.h | 18 + lib/Driver/Tools.cpp | 44 +- lib/Frontend/CompilerInvocation.cpp | 45 +- lib/Frontend/InitPreprocessor.cpp | 145 +- lib/Lex/PPMacroExpansion.cpp | 5 + lib/Parse/ParseDecl.cpp | 4 +- lib/Parse/ParseExpr.cpp | 32 +- lib/Parse/ParseObjc.cpp | 42 +- lib/Rewrite/FixItRewriter.cpp | 3 +- lib/Rewrite/Rewriter.cpp | 73 +- lib/Sema/AttributeList.cpp | 5 + lib/Sema/DelayedDiagnostic.cpp | 3 + lib/Sema/JumpDiagnostics.cpp | 144 +- lib/Sema/Sema.cpp | 40 +- lib/Sema/SemaCXXCast.cpp | 137 +- lib/Sema/SemaChecking.cpp | 282 ++- lib/Sema/SemaCodeComplete.cpp | 97 +- lib/Sema/SemaDecl.cpp | 240 ++- lib/Sema/SemaDeclAttr.cpp | 112 +- lib/Sema/SemaDeclCXX.cpp | 45 +- lib/Sema/SemaDeclObjC.cpp | 429 ++++- lib/Sema/SemaExpr.cpp | 229 ++- lib/Sema/SemaExprCXX.cpp | 308 +++- lib/Sema/SemaExprObjC.cpp | 409 ++++- lib/Sema/SemaInit.cpp | 315 +++- lib/Sema/SemaObjCProperty.cpp | 383 ++++- lib/Sema/SemaOverload.cpp | 298 +++- lib/Sema/SemaStmt.cpp | 51 +- lib/Sema/SemaTemplate.cpp | 15 +- lib/Sema/SemaTemplateDeduction.cpp | 7 + lib/Sema/SemaType.cpp | 313 +++- lib/Sema/TreeTransform.h | 84 +- lib/Sema/TypeLocBuilder.h | 9 + lib/Serialization/ASTReader.cpp | 15 +- lib/Serialization/ASTReaderStmt.cpp | 34 +- lib/Serialization/ASTWriter.cpp | 3 + lib/Serialization/ASTWriterStmt.cpp | 26 + .../Checkers/DeadStoresChecker.cpp | 6 +- .../Checkers/StackAddrEscapeChecker.cpp | 20 +- lib/StaticAnalyzer/Core/CFRefCount.cpp | 107 +- lib/StaticAnalyzer/Core/ExprEngine.cpp | 47 +- test/Analysis/objc-arc.m | 149 ++ test/Analysis/pr4209.m | 6 +- test/CodeGenObjC/arc-arm.m | 20 + test/CodeGenObjC/arc-block-ivar-layout.m | 60 + test/CodeGenObjC/arc-bridged-cast.m | 90 + test/CodeGenObjC/arc-foreach.m | 27 + test/CodeGenObjC/arc-ivar-layout.m | 44 + test/CodeGenObjC/arc-no-runtime.m | 9 + test/CodeGenObjC/arc-related-result-type.m | 30 + test/CodeGenObjC/arc-unopt.m | 69 + test/CodeGenObjC/arc-weak-property.m | 55 + test/CodeGenObjC/arc.m | 1511 +++++++++++++++++ test/CodeGenObjC/autorelease.m | 30 + test/CodeGenObjC/block-6.m | 14 +- test/CodeGenObjC/mrr-autorelease.m | 24 + test/CodeGenObjC/nonlazy-msgSend.m | 6 + test/CodeGenObjC/related-result-type.m | 54 + test/CodeGenObjCXX/arc-globals.mm | 27 + test/CodeGenObjCXX/arc-mangle.mm | 25 + test/CodeGenObjCXX/arc-new-delete.mm | 86 + test/CodeGenObjCXX/arc-pseudo-destructors.mm | 21 + test/CodeGenObjCXX/arc-references.mm | 53 + .../arc-special-member-functions.mm | 154 ++ test/CodeGenObjCXX/arc.mm | 152 ++ test/CodeGenObjCXX/copy.mm | 26 + test/Driver/arc-exceptions.m | 5 + test/Driver/arc.c | 14 + test/Driver/ios-simulator-arcruntime.c | 5 + test/Driver/no-objc-arr.m | 8 + test/Index/arc-annotate.m | 38 + test/Index/arc-complete.m | 16 + test/Index/complete-exprs.m | 9 +- test/Index/complete-property-flags.m | 2 + test/Lexer/has_feature_objc_arc.m | 20 + test/PCH/Inputs/arc.h | 20 + test/PCH/arc.m | 9 + test/SemaObjC/Inputs/arc-system-header.h | 42 + test/SemaObjC/arc-bridged-cast.m | 40 + test/SemaObjC/arc-decls.m | 64 + test/SemaObjC/arc-no-runtime.m | 15 + test/SemaObjC/arc-non-pod-memaccess.m | 55 + test/SemaObjC/arc-property-decl-attrs.m | 67 + test/SemaObjC/arc-property-lifetime.m | 112 ++ test/SemaObjC/arc-property.m | 48 + test/SemaObjC/arc-system-header.m | 48 + test/SemaObjC/arc-type-conversion.m | 55 + test/SemaObjC/arc-unsafe_unretained.m | 12 + test/SemaObjC/arc.m | 550 ++++++ test/SemaObjC/autoreleasepool.m | 22 + test/SemaObjC/class-unavail-warning.m | 2 +- test/SemaObjC/error-property-gc-attr.m | 4 +- test/SemaObjC/property-10.m | 17 +- test/SemaObjC/special-dep-unavail-warning.m | 10 +- test/SemaObjC/synthesized-ivar.m | 8 + test/SemaObjC/warn-retain-cycle.m | 91 + test/SemaObjC/weak-property.m | 24 + test/SemaObjCXX/Inputs/arc-system-header.h | 14 + test/SemaObjCXX/arc-0x.mm | 28 + test/SemaObjCXX/arc-bool-conversion.mm | 7 + test/SemaObjCXX/arc-bridged-cast.mm | 36 + test/SemaObjCXX/arc-libcxx.mm | 11 + test/SemaObjCXX/arc-libstdcxx.mm | 10 + test/SemaObjCXX/arc-memfunc.mm | 15 + test/SemaObjCXX/arc-non-pod.mm | 115 ++ test/SemaObjCXX/arc-object-init-destroy.mm | 52 + test/SemaObjCXX/arc-overloading.mm | 175 ++ test/SemaObjCXX/arc-system-header.mm | 10 + test/SemaObjCXX/arc-templates.mm | 219 +++ test/SemaObjCXX/arc-type-conversion.mm | 198 +++ test/SemaObjCXX/arc-type-traits.mm | 90 + tools/libclang/CIndex.cpp | 6 +- tools/libclang/CXCursor.cpp | 5 +- tools/libclang/CXType.cpp | 6 +- 198 files changed, 13876 insertions(+), 1101 deletions(-) create mode 100644 test/Analysis/objc-arc.m create mode 100644 test/CodeGenObjC/arc-arm.m create mode 100644 test/CodeGenObjC/arc-block-ivar-layout.m create mode 100644 test/CodeGenObjC/arc-bridged-cast.m create mode 100644 test/CodeGenObjC/arc-foreach.m create mode 100644 test/CodeGenObjC/arc-ivar-layout.m create mode 100644 test/CodeGenObjC/arc-no-runtime.m create mode 100644 test/CodeGenObjC/arc-related-result-type.m create mode 100644 test/CodeGenObjC/arc-unopt.m create mode 100644 test/CodeGenObjC/arc-weak-property.m create mode 100644 test/CodeGenObjC/arc.m create mode 100644 test/CodeGenObjC/autorelease.m create mode 100644 test/CodeGenObjC/mrr-autorelease.m create mode 100644 test/CodeGenObjC/nonlazy-msgSend.m create mode 100644 test/CodeGenObjC/related-result-type.m create mode 100644 test/CodeGenObjCXX/arc-globals.mm create mode 100644 test/CodeGenObjCXX/arc-mangle.mm create mode 100644 test/CodeGenObjCXX/arc-new-delete.mm create mode 100644 test/CodeGenObjCXX/arc-pseudo-destructors.mm create mode 100644 test/CodeGenObjCXX/arc-references.mm create mode 100644 test/CodeGenObjCXX/arc-special-member-functions.mm create mode 100644 test/CodeGenObjCXX/arc.mm create mode 100644 test/CodeGenObjCXX/copy.mm create mode 100644 test/Driver/arc-exceptions.m create mode 100644 test/Driver/arc.c create mode 100644 test/Driver/ios-simulator-arcruntime.c create mode 100644 test/Driver/no-objc-arr.m create mode 100644 test/Index/arc-annotate.m create mode 100644 test/Index/arc-complete.m create mode 100644 test/Lexer/has_feature_objc_arc.m create mode 100644 test/PCH/Inputs/arc.h create mode 100644 test/PCH/arc.m create mode 100644 test/SemaObjC/Inputs/arc-system-header.h create mode 100644 test/SemaObjC/arc-bridged-cast.m create mode 100644 test/SemaObjC/arc-decls.m create mode 100644 test/SemaObjC/arc-no-runtime.m create mode 100644 test/SemaObjC/arc-non-pod-memaccess.m create mode 100644 test/SemaObjC/arc-property-decl-attrs.m create mode 100644 test/SemaObjC/arc-property-lifetime.m create mode 100644 test/SemaObjC/arc-property.m create mode 100644 test/SemaObjC/arc-system-header.m create mode 100644 test/SemaObjC/arc-type-conversion.m create mode 100644 test/SemaObjC/arc-unsafe_unretained.m create mode 100644 test/SemaObjC/arc.m create mode 100644 test/SemaObjC/autoreleasepool.m create mode 100644 test/SemaObjC/warn-retain-cycle.m create mode 100644 test/SemaObjC/weak-property.m create mode 100644 test/SemaObjCXX/Inputs/arc-system-header.h create mode 100644 test/SemaObjCXX/arc-0x.mm create mode 100644 test/SemaObjCXX/arc-bool-conversion.mm create mode 100644 test/SemaObjCXX/arc-bridged-cast.mm create mode 100644 test/SemaObjCXX/arc-libcxx.mm create mode 100644 test/SemaObjCXX/arc-libstdcxx.mm create mode 100644 test/SemaObjCXX/arc-memfunc.mm create mode 100644 test/SemaObjCXX/arc-non-pod.mm create mode 100644 test/SemaObjCXX/arc-object-init-destroy.mm create mode 100644 test/SemaObjCXX/arc-overloading.mm create mode 100644 test/SemaObjCXX/arc-system-header.mm create mode 100644 test/SemaObjCXX/arc-templates.mm create mode 100644 test/SemaObjCXX/arc-type-conversion.mm create mode 100644 test/SemaObjCXX/arc-type-traits.mm diff --git a/docs/LanguageExtensions.html b/docs/LanguageExtensions.html index 8f43725d82..cce2e8c5b0 100644 --- a/docs/LanguageExtensions.html +++ b/docs/LanguageExtensions.html @@ -65,6 +65,7 @@ td {
  • Objective-C Features
  • Function Overloading in C
  • @@ -669,6 +670,12 @@ property access via the given method. In all other respects, a method with a related result type is treated the same way as method without a related result type.

    + +

    Automatic reference counting

    + + +

    Clang provides support for automated reference counting in Objective-C, which eliminates the need for manual retain/release/autorelease message sends. There are two feature macros associated with automatic reference counting: __has_feature(objc_arc) indicates the availability of automated reference counting in general, while __has_feature(objc_arc_weak) indicates that automated reference counting also includes support for __weak pointers to Objective-C objects.

    +

    Function Overloading in C

    diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 517c25df24..468343d2a0 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -993,6 +993,18 @@ public: return getExtQualType(T, Qs); } + /// getLifetimeQualifiedType - Returns a type with the given + /// lifetime qualifier. + QualType getLifetimeQualifiedType(QualType type, + Qualifiers::ObjCLifetime lifetime) { + assert(type.getObjCLifetime() == Qualifiers::OCL_None); + assert(lifetime != Qualifiers::OCL_None); + + Qualifiers qs; + qs.addObjCLifetime(lifetime); + return getQualifiedType(type, qs); + } + DeclarationNameInfo getNameForTemplate(TemplateName Name, SourceLocation NameLoc) const; @@ -1044,7 +1056,9 @@ public: /// isObjCNSObjectType - Return true if this is an NSObject object with /// its NSObject attribute set. - bool isObjCNSObjectType(QualType Ty) const; + static bool isObjCNSObjectType(QualType Ty) { + return Ty->isObjCNSObjectType(); + } //===--------------------------------------------------------------------===// // Type Sizing and Analysis diff --git a/include/clang/AST/CanonicalType.h b/include/clang/AST/CanonicalType.h index 2518814904..38e6b41977 100644 --- a/include/clang/AST/CanonicalType.h +++ b/include/clang/AST/CanonicalType.h @@ -250,7 +250,6 @@ public: LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isObjectType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isIncompleteType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isIncompleteOrObjectType) - LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isPODType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isVariablyModifiedType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isIntegerType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isEnumeralType) @@ -295,6 +294,7 @@ public: LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isUnsignedIntegerOrEnumerationType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isConstantSizeType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isSpecifierType) + LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(CXXRecordDecl*, getAsCXXRecordDecl) /// \brief Retrieve the proxy-adaptor type. /// diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index 74ceb43c71..2c12b837bd 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -1240,6 +1240,9 @@ class ObjCImplementationDecl : public ObjCImplDecl { /// IvarInitializers - The arguments used to initialize the ivars CXXCtorInitializer **IvarInitializers; unsigned NumIvarInitializers; + + /// true if class has a .cxx_[construct,destruct] method. + bool HasCXXStructors : 1; /// true of class extension has at least one bitfield ivar. bool HasSynthBitfield : 1; @@ -1249,7 +1252,7 @@ class ObjCImplementationDecl : public ObjCImplDecl { ObjCInterfaceDecl *superDecl) : ObjCImplDecl(ObjCImplementation, DC, L, classInterface), SuperClass(superDecl), IvarInitializers(0), NumIvarInitializers(0), - HasSynthBitfield(false) {} + HasCXXStructors(false), HasSynthBitfield(false) {} public: static ObjCImplementationDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L, @@ -1287,6 +1290,9 @@ public: void setIvarInitializers(ASTContext &C, CXXCtorInitializer ** initializers, unsigned numInitializers); + + bool hasCXXStructors() const { return HasCXXStructors; } + void setHasCXXStructors(bool val) { HasCXXStructors = val; } bool hasSynthBitfield() const { return HasSynthBitfield; } void setHasSynthBitfield (bool val) { HasSynthBitfield = val; } @@ -1393,7 +1399,10 @@ public: OBJC_PR_copy = 0x20, OBJC_PR_nonatomic = 0x40, OBJC_PR_setter = 0x80, - OBJC_PR_atomic = 0x100 + OBJC_PR_atomic = 0x100, + OBJC_PR_weak = 0x200, + OBJC_PR_strong = 0x400, + OBJC_PR_unsafe_unretained = 0x800 }; enum SetterKind { Assign, Retain, Copy }; @@ -1401,8 +1410,8 @@ public: private: SourceLocation AtLoc; // location of @property TypeSourceInfo *DeclType; - unsigned PropertyAttributes : 9; - unsigned PropertyAttributesAsWritten : 9; + unsigned PropertyAttributes : 11; + unsigned PropertyAttributesAsWritten : 11; // @required/@optional unsigned PropertyImplementation : 2; @@ -1466,7 +1475,7 @@ public: /// the property setter. This is only valid if the property has been /// defined to have a setter. SetterKind getSetterKind() const { - if (PropertyAttributes & OBJC_PR_retain) + if (PropertyAttributes & (OBJC_PR_retain|OBJC_PR_strong)) return Retain; if (PropertyAttributes & OBJC_PR_copy) return Copy; diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index ce86458ed4..df20cb4057 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -2276,6 +2276,8 @@ private: case CK_IntegralComplexToReal: case CK_IntegralComplexCast: case CK_IntegralComplexToFloatingComplex: + case CK_ObjCProduceObject: + case CK_ObjCConsumeObject: assert(!getType()->isBooleanType() && "unheralded conversion to bool"); // fallthrough to check for null base path diff --git a/include/clang/AST/ExprObjC.h b/include/clang/AST/ExprObjC.h index 8163923d62..472a7efcda 100644 --- a/include/clang/AST/ExprObjC.h +++ b/include/clang/AST/ExprObjC.h @@ -456,7 +456,11 @@ class ObjCMessageExpr : public Expr { /// /// When non-zero, we have a method declaration; otherwise, we just /// have a selector. - unsigned HasMethod : 8; + unsigned HasMethod : 1; + + /// \brief Whether this message send is a "delegate init call", + /// i.e. a call of an init method on self from within an init method. + unsigned IsDelegateInitCall : 1; /// \brief When the message expression is a send to 'super', this is /// the location of the 'super' keyword. @@ -476,7 +480,7 @@ class ObjCMessageExpr : public Expr { ObjCMessageExpr(EmptyShell Empty, unsigned NumArgs) : Expr(ObjCMessageExprClass, Empty), NumArgs(NumArgs), Kind(0), - HasMethod(0), SelectorOrMethod(0) { } + HasMethod(0), IsDelegateInitCall(0), SelectorOrMethod(0) { } ObjCMessageExpr(QualType T, ExprValueKind VK, SourceLocation LBracLoc, @@ -807,6 +811,12 @@ public: getArgs()[Arg] = ArgExpr; } + /// isDelegateInitCall - Answers whether this message send has been + /// tagged as a "delegate init call", i.e. a call to a method in the + /// -init family on self from within an -init method implementation. + bool isDelegateInitCall() const { return IsDelegateInitCall; } + void setDelegateInitCall(bool isDelegate) { IsDelegateInitCall = isDelegate; } + SourceLocation getLeftLoc() const { return LBracLoc; } SourceLocation getRightLoc() const { return RBracLoc; } SourceLocation getSelectorLoc() const { return SelectorLoc; } @@ -892,6 +902,122 @@ public: child_range children() { return child_range(&Base, &Base+1); } }; + +/// ObjCIndirectCopyRestoreExpr - Represents the passing of a function +/// argument by indirect copy-restore in ARC. This is used to support +/// passing indirect arguments with the wrong lifetime, e.g. when +/// passing the address of a __strong local variable to an 'out' +/// parameter. This expression kind is only valid in an "argument" +/// position to some sort of call expression. +/// +/// The parameter must have type 'pointer to T', and the argument must +/// have type 'pointer to U', where T and U agree except possibly in +/// qualification. If the argument value is null, then a null pointer +/// is passed; otherwise it points to an object A, and: +/// 1. A temporary object B of type T is initialized, either by +/// zero-initialization (used when initializing an 'out' parameter) +/// or copy-initialization (used when initializing an 'inout' +/// parameter). +/// 2. The address of the temporary is passed to the function. +/// 3. If the call completes normally, A is move-assigned from B. +/// 4. Finally, A is destroyed immediately. +/// +/// Currently 'T' must be a retainable object lifetime and must be +/// __autoreleasing; this qualifier is ignored when initializing +/// the value. +class ObjCIndirectCopyRestoreExpr : public Expr { + Stmt *Operand; + + // unsigned ObjCIndirectCopyRestoreBits.ShouldCopy : 1; + + friend class ASTReader; + friend class ASTStmtReader; + + void setShouldCopy(bool shouldCopy) { + ObjCIndirectCopyRestoreExprBits.ShouldCopy = shouldCopy; + } + + explicit ObjCIndirectCopyRestoreExpr(EmptyShell Empty) + : Expr(ObjCIndirectCopyRestoreExprClass, Empty) { } + +public: + ObjCIndirectCopyRestoreExpr(Expr *operand, QualType type, bool shouldCopy) + : Expr(ObjCIndirectCopyRestoreExprClass, type, VK_LValue, OK_Ordinary, + operand->isTypeDependent(), operand->isValueDependent(), + operand->containsUnexpandedParameterPack()), + Operand(operand) { + setShouldCopy(shouldCopy); + } + + Expr *getSubExpr() { return cast(Operand); } + const Expr *getSubExpr() const { return cast(Operand); } + + /// shouldCopy - True if we should do the 'copy' part of the + /// copy-restore. If false, the temporary will be zero-initialized. + bool shouldCopy() const { return ObjCIndirectCopyRestoreExprBits.ShouldCopy; } + + child_range children() { return child_range(&Operand, &Operand+1); } + + // Source locations are determined by the subexpression. + SourceRange getSourceRange() const { return Operand->getSourceRange(); } + SourceLocation getExprLoc() const { return getSubExpr()->getExprLoc(); } + + static bool classof(const Stmt *s) { + return s->getStmtClass() == ObjCIndirectCopyRestoreExprClass; + } + static bool classof(const ObjCIndirectCopyRestoreExpr *) { return true; } +}; + +/// \brief An Objective-C "bridged" cast expression, which casts between +/// Objective-C pointers and C pointers, transferring ownership in the process. +/// +/// \code +/// NSString *str = (__bridge_transfer NSString *)CFCreateString(); +/// \endcode +class ObjCBridgedCastExpr : public ExplicitCastExpr { + SourceLocation LParenLoc; + SourceLocation BridgeKeywordLoc; + unsigned Kind : 2; + + friend class ASTStmtReader; + friend class ASTStmtWriter; + +public: + ObjCBridgedCastExpr(SourceLocation LParenLoc, ObjCBridgeCastKind Kind, + SourceLocation BridgeKeywordLoc, TypeSourceInfo *TSInfo, + Expr *Operand) + : ExplicitCastExpr(ObjCBridgedCastExprClass, TSInfo->getType(), VK_RValue, + CK_BitCast, Operand, 0, TSInfo), + LParenLoc(LParenLoc), BridgeKeywordLoc(BridgeKeywordLoc), Kind(Kind) { } + + /// \brief Construct an empty Objective-C bridged cast. + explicit ObjCBridgedCastExpr(EmptyShell Shell) + : ExplicitCastExpr(ObjCBridgedCastExprClass, Shell, 0) { } + + SourceLocation getLParenLoc() const { return LParenLoc; } + + /// \brief Determine which kind of bridge is being performed via this cast. + ObjCBridgeCastKind getBridgeKind() const { + return static_cast(Kind); + } + + /// \brief Retrieve the kind of bridge being performed as a string. + llvm::StringRef getBridgeKindName() const; + + /// \brief The location of the bridge keyword. + SourceLocation getBridgeKeywordLoc() const { return BridgeKeywordLoc; } + + SourceRange getSourceRange() const { + return SourceRange(LParenLoc, getSubExpr()->getLocEnd()); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ObjCBridgedCastExprClass; + } + static bool classof(const ObjCBridgedCastExpr *) { return true; } + +}; + } // end namespace clang #endif diff --git a/include/clang/AST/OperationKinds.h b/include/clang/AST/OperationKinds.h index 35c72c45ce..004426e89a 100644 --- a/include/clang/AST/OperationKinds.h +++ b/include/clang/AST/OperationKinds.h @@ -245,7 +245,17 @@ enum CastKind { /// \brief Converts from an integral complex to a floating complex. /// _Complex unsigned -> _Complex float - CK_IntegralComplexToFloatingComplex + CK_IntegralComplexToFloatingComplex, + + /// \brief Produces an Objective-C object so that it may be + /// consumed, e.g. by being passed to a consuming parameter. Calls + /// objc_retain. + CK_ObjCProduceObject, + + /// \brief Consumes an Objective-C object that has just been + /// produced, e.g. as the return value of a retaining call. Enters + /// a cleanup to call objc_release at some indefinite time. + CK_ObjCConsumeObject }; #define CK_Invalid ((CastKind) -1) @@ -284,6 +294,19 @@ enum UnaryOperatorKind { UO_Extension // __extension__ marker. }; +/// \brief The kind of bridging performed by the Objective-C bridge cast. +enum ObjCBridgeCastKind { + /// \brief Bridging via __bridge, which does nothing but reinterpret + /// the bits. + OBC_Bridge, + /// \brief Bridging via __bridge_transfer, which transfers ownership of an + /// Objective-C pointer into ARC. + OBC_BridgeTransfer, + /// \brief Bridging via __bridge_retain, which makes an ARC object available + /// as a +1 C pointer. + OBC_BridgeRetained +}; + } #endif diff --git a/include/clang/AST/ParentMap.h b/include/clang/AST/ParentMap.h index 9ea5a0930d..22c1e7269f 100644 --- a/include/clang/AST/ParentMap.h +++ b/include/clang/AST/ParentMap.h @@ -32,6 +32,7 @@ public: Stmt *getParent(Stmt*) const; Stmt *getParentIgnoreParens(Stmt *) const; Stmt *getParentIgnoreParenCasts(Stmt *) const; + Stmt *getOuterParenParent(Stmt *) const; const Stmt *getParent(const Stmt* S) const { return getParent(const_cast(S)); diff --git a/include/clang/AST/PrettyPrinter.h b/include/clang/AST/PrettyPrinter.h index cf5fadbd18..fc8ac36b3b 100644 --- a/include/clang/AST/PrettyPrinter.h +++ b/include/clang/AST/PrettyPrinter.h @@ -41,7 +41,7 @@ struct PrintingPolicy { SuppressTagKeyword(false), SuppressTag(false), SuppressScope(false), SuppressInitializers(false), Dump(false), ConstantArraySizeAsWritten(false), - AnonymousTagLocations(true) { } + AnonymousTagLocations(true), SuppressStrongLifetime(false) { } /// \brief The number of spaces to use to indent each line. unsigned Indentation : 8; @@ -129,6 +129,10 @@ struct PrintingPolicy { /// that entity (e.g., "enum "). Otherwise, just /// prints "" for the name. bool AnonymousTagLocations : 1; + + /// \brief When true, suppress printing of the __strong lifetime qualifier in + /// ARC. + unsigned SuppressStrongLifetime : 1; }; } // end namespace clang diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index a8f182a5bc..1a30df6d8b 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -1721,6 +1721,7 @@ DEF_TRAVERSE_STMT(ObjCAtSynchronizedStmt, { }) DEF_TRAVERSE_STMT(ObjCAtThrowStmt, { }) DEF_TRAVERSE_STMT(ObjCAtTryStmt, { }) DEF_TRAVERSE_STMT(ObjCForCollectionStmt, { }) +DEF_TRAVERSE_STMT(ObjCAutoreleasePoolStmt, { }) DEF_TRAVERSE_STMT(CXXForRangeStmt, { }) DEF_TRAVERSE_STMT(ReturnStmt, { }) DEF_TRAVERSE_STMT(SwitchStmt, { }) @@ -1933,6 +1934,10 @@ DEF_TRAVERSE_STMT(ObjCMessageExpr, { }) DEF_TRAVERSE_STMT(ObjCPropertyRefExpr, { }) DEF_TRAVERSE_STMT(ObjCProtocolExpr, { }) DEF_TRAVERSE_STMT(ObjCSelectorExpr, { }) +DEF_TRAVERSE_STMT(ObjCIndirectCopyRestoreExpr, { }) +DEF_TRAVERSE_STMT(ObjCBridgedCastExpr, { + TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc())); +}) DEF_TRAVERSE_STMT(ParenExpr, { }) DEF_TRAVERSE_STMT(ParenListExpr, { }) DEF_TRAVERSE_STMT(PredefinedExpr, { }) diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h index 695fb0403e..2fe2a20e49 100644 --- a/include/clang/AST/Stmt.h +++ b/include/clang/AST/Stmt.h @@ -183,6 +183,13 @@ protected: unsigned NumPreArgs : 1; }; + class ObjCIndirectCopyRestoreExprBitfields { + friend class ObjCIndirectCopyRestoreExpr; + unsigned : NumExprBits; + + unsigned ShouldCopy : 1; + }; + union { // FIXME: this is wasteful on 64-bit platforms. void *Aligner; @@ -193,6 +200,7 @@ protected: DeclRefExprBitfields DeclRefExprBits; CastExprBitfields CastExprBits; CallExprBitfields CallExprBits; + ObjCIndirectCopyRestoreExprBitfields ObjCIndirectCopyRestoreExprBits; }; friend class ASTStmtReader; diff --git a/include/clang/AST/StmtObjC.h b/include/clang/AST/StmtObjC.h index 1800a71f91..d996fc5cad 100644 --- a/include/clang/AST/StmtObjC.h +++ b/include/clang/AST/StmtObjC.h @@ -342,6 +342,39 @@ public: child_range children() { return child_range(&Throw, &Throw+1); } }; +/// ObjCAutoreleasePoolStmt - This represent objective-c's +/// @autoreleasepool Statement +class ObjCAutoreleasePoolStmt : public Stmt { + Stmt *SubStmt; + SourceLocation AtLoc; +public: + ObjCAutoreleasePoolStmt(SourceLocation atLoc, + Stmt *subStmt) + : Stmt(ObjCAutoreleasePoolStmtClass), + SubStmt(subStmt), AtLoc(atLoc) {} + + explicit ObjCAutoreleasePoolStmt(EmptyShell Empty) : + Stmt(ObjCAutoreleasePoolStmtClass, Empty) { } + + const Stmt *getSubStmt() const { return SubStmt; } + Stmt *getSubStmt() { return SubStmt; } + void setSubStmt(Stmt *S) { SubStmt = S; } + + SourceRange getSourceRange() const { + return SourceRange(AtLoc, SubStmt->getLocEnd()); + } + + SourceLocation getAtLoc() const { return AtLoc; } + void setAtLoc(SourceLocation Loc) { AtLoc = Loc; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ObjCAutoreleasePoolStmtClass; + } + static bool classof(const ObjCAutoreleasePoolStmt *) { return true; } + + child_range children() { return child_range(&SubStmt, &SubStmt + 1); } +}; + } // end namespace clang #endif diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 77633831ff..26632c37bd 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -126,6 +126,28 @@ public: Strong }; + enum ObjCLifetime { + /// There is no lifetime qualification on this type. + OCL_None, + + /// This object can be modified without requiring retains or + /// releases. + OCL_ExplicitNone, + + /// Assigning into this object requires the old value to be + /// released and the new value to be retained. The timing of the + /// release of the old value is inexact: it may be moved to + /// immediately after the last known point where the value is + /// live. + OCL_Strong, + + /// Reading or writing from this object requires a barrier call. + OCL_Weak, + + /// Assigning into this object requires a lifetime extension. + OCL_Autoreleasing + }; + enum { /// The maximum supported address space number. /// 24 bits should be enough for anyone. @@ -218,7 +240,37 @@ public: qs.removeObjCGCAttr(); return qs; } + Qualifiers withoutObjCGLifetime() const { + Qualifiers qs = *this; + qs.removeObjCLifetime(); + return qs; + } + bool hasObjCLifetime() const { return Mask & LifetimeMask; } + ObjCLifetime getObjCLifetime() const { + return ObjCLifetime((Mask & LifetimeMask) >> LifetimeShift); + } + void setObjCLifetime(ObjCLifetime type) { + Mask = (Mask & ~LifetimeMask) | (type << LifetimeShift); + } + void removeObjCLifetime() { setObjCLifetime(OCL_None); } + void addObjCLifetime(ObjCLifetime type) { + assert(type); + setObjCLifetime(type); + } + + /// True if the lifetime is neither None or ExplicitNone. + bool hasNonTrivialObjCLifetime() const { + ObjCLifetime lifetime = getObjCLifetime(); + return (lifetime > OCL_ExplicitNone); + } + + /// True if the lifetime is either strong or weak. + bool hasStrongOrWeakObjCLifetime() const { + ObjCLifetime lifetime = getObjCLifetime(); + return (lifetime == OCL_Strong || lifetime == OCL_Weak); + } + bool hasAddressSpace() const { return Mask & AddressSpaceMask; } unsigned getAddressSpace() const { return Mask >> AddressSpaceShift; } void setAddressSpace(unsigned space) { @@ -277,6 +329,8 @@ public: addAddressSpace(Q.getAddressSpace()); if (Q.hasObjCGCAttr()) addObjCGCAttr(Q.getObjCGCAttr()); + if (Q.hasObjCLifetime()) + addObjCLifetime(Q.getObjCLifetime()); } } @@ -287,6 +341,8 @@ public: !hasAddressSpace() || !qs.hasAddressSpace()); assert(getObjCGCAttr() == qs.getObjCGCAttr() || !hasObjCGCAttr() || !qs.hasObjCGCAttr()); + assert(getObjCLifetime() == qs.getObjCLifetime() || + !hasObjCLifetime() || !qs.hasObjCLifetime()); Mask |= qs.Mask; } @@ -301,10 +357,30 @@ public: // changed. (getObjCGCAttr() == other.getObjCGCAttr() || !hasObjCGCAttr() || !other.hasObjCGCAttr()) && + // ObjC lifetime qualifiers must match exactly. + getObjCLifetime() == other.getObjCLifetime() && // CVR qualifiers may subset. (((Mask & CVRMask) | (other.Mask & CVRMask)) == (Mask & CVRMask)); } + /// \brief Determines if these qualifiers compatibly include another set of + /// qualifiers from the narrow perspective of Objective-C ARC lifetime. + /// + /// One set of Objective-C lifetime qualifiers compatibly includes the other + /// if the lifetime qualifiers match, or if both are non-__weak and the + /// including set also contains the 'const' qualifier. + bool compatiblyIncludesObjCLifetime(Qualifiers other) const { + if (getObjCLifetime() == other.getObjCLifetime()) + return true; + + if (getObjCLifetime() == OCL_Weak || other.getObjCLifetime() == OCL_Weak) + return false; + + return hasConst(); + } + + bool isSupersetOf(Qualifiers Other) const; + /// \brief Determine whether this set of qualifiers is a strict superset of /// another set of qualifiers, not considering qualifier compatibility. bool isStrictSupersetOf(Qualifiers Other) const; @@ -351,14 +427,16 @@ public: private: - // bits: |0 1 2|3 .. 4|5 .. 31| - // |C R V|GCAttr|AddrSpace| + // bits: |0 1 2|3 .. 4|5 .. 7|8 ... 31| + // |C R V|GCAttr|Lifetime|AddressSpace| uint32_t Mask; static const uint32_t GCAttrMask = 0x18; static const uint32_t GCAttrShift = 3; - static const uint32_t AddressSpaceMask = ~(CVRMask | GCAttrMask); - static const uint32_t AddressSpaceShift = 5; + static const uint32_t LifetimeMask = 0xE0; + static const uint32_t LifetimeShift = 5; + static const uint32_t AddressSpaceMask = ~(CVRMask|GCAttrMask|LifetimeMask); + static const uint32_t AddressSpaceShift = 8; }; /// CallingConv - Specifies the calling convention that a function uses. @@ -527,6 +605,23 @@ public: return QualType::isConstant(*this, Ctx); } + /// \brief Determine whether this is a Plain Old Data (POD) type (C++ 3.9p10). + bool isPODType(ASTContext &Context) const; + + /// isCXX11PODType() - Return true if this is a POD type according to the + /// more relaxed rules of the C++11 standard, regardless of the current + /// compilation's language. + /// (C++0x [basic.types]p9) + bool isCXX11PODType(ASTContext &Context) const; + + /// isTrivialType - Return true if this is a trivial type + /// (C++0x [basic.types]p9) + bool isTrivialType(ASTContext &Context) const; + + /// isTriviallyCopyableType - Return true if this is a trivially + /// copyable type (C++0x [basic.types]p9) + bool isTriviallyCopyableType(ASTContext &Context) const; + // Don't promise in the API that anything besides 'const' can be // easily added. @@ -709,7 +804,7 @@ public: /// getAddressSpace - Return the address space of this type. inline unsigned getAddressSpace() const; - /// GCAttrTypesAttr - Returns gc attribute of this type. + /// getObjCGCAttr - Returns gc attribute of this type. inline Qualifiers::GC getObjCGCAttr() const; /// isObjCGCWeak true when Type is objc's weak. @@ -722,9 +817,24 @@ public: return getObjCGCAttr() == Qualifiers::Strong; } + /// getObjCLifetime - Returns lifetime attribute of this type. + Qualifiers::ObjCLifetime getObjCLifetime() const { + return getQualifiers().getObjCLifetime(); + } + + bool hasNonTrivialObjCLifetime() const { + return getQualifiers().hasNonTrivialObjCLifetime(); + } + + bool hasStrongOrWeakObjCLifetime() const { + return getQualifiers().hasStrongOrWeakObjCLifetime(); + } + enum DestructionKind { DK_none, - DK_cxx_destructor + DK_cxx_destructor, + DK_objc_strong_lifetime, + DK_objc_weak_lifetime }; /// isDestructedType - nonzero if objects of this type require @@ -735,6 +845,9 @@ public: return isDestructedTypeImpl(*this); } + /// \brief Determine whether this type has trivial copy-assignment semantics. + bool hasTrivialCopyAssignment(ASTContext &Context) const; + private: // These methods are implemented in a separate translation unit; // "static"-ize them to avoid creating temporary QualTypes in the @@ -849,6 +962,11 @@ public: bool hasObjCGCAttr() const { return Quals.hasObjCGCAttr(); } Qualifiers::GC getObjCGCAttr() const { return Quals.getObjCGCAttr(); } + bool hasObjCLifetime() const { return Quals.hasObjCLifetime(); } + Qualifiers::ObjCLifetime getObjCLifetime() const { + return Quals.getObjCLifetime(); + } + bool hasAddressSpace() const { return Quals.hasAddressSpace(); } unsigned getAddressSpace() const { return Quals.getAddressSpace(); } @@ -1005,7 +1123,7 @@ protected: /// Extra information which affects how the function is called, like /// regparm and the calling convention. - unsigned ExtInfo : 8; + unsigned ExtInfo : 9; /// Whether the function is variadic. Only used by FunctionProtoType. unsigned Variadic : 1; @@ -1186,31 +1304,14 @@ public: return !isReferenceType() && !isFunctionType() && !isVoidType(); } - /// isPODType - Return true if this is a plain-old-data type (C++ 3.9p10). - bool isPODType() const; - /// isLiteralType - Return true if this is a literal type /// (C++0x [basic.types]p10) bool isLiteralType() const; - /// isTrivialType - Return true if this is a trivial type - /// (C++0x [basic.types]p9) - bool isTrivialType() const; - - /// isTriviallyCopyableType - Return true if this is a trivially copyable type - /// (C++0x [basic.types]p9 - bool isTriviallyCopyableType() const; - /// \brief Test if this type is a standard-layout type. /// (C++0x [basic.type]p9) bool isStandardLayoutType() const; - /// isCXX11PODType() - Return true if this is a POD type according to the - /// more relaxed rules of the C++11 standard, regardless of the current - /// compilation's language. - /// (C++0x [basic.types]p9) - bool isCXX11PODType() const; - /// Helper methods to distinguish type categories. All type predicates /// operate on the canonical type, ignoring typedefs and qualifiers. @@ -1290,7 +1391,11 @@ public: bool isComplexIntegerType() const; // GCC _Complex integer type. bool isVectorType() const; // GCC vector type. bool isExtVectorType() const; // Extended vector type. - bool isObjCObjectPointerType() const; // Pointer to *any* ObjC object. + bool isObjCObjectPointerType() const; // pointer to ObjC object + bool isObjCRetainableType() const; // ObjC object or block pointer + bool isObjCLifetimeType() const; // (array of)* retainable type + bool isObjCIndirectLifetimeType() const; // (pointer to)* lifetime type + bool isObjCNSObjectType() const; // __attribute__((NSObject)) // FIXME: change this to 'raw' interface type, so we can used 'interface' type // for the common case. bool isObjCObjectType() const; // NSString or typeof(*(id)0) @@ -1302,9 +1407,19 @@ public: bool isObjCClassType() const; // Class bool isObjCSelType() const; // Class bool isObjCBuiltinType() const; // 'id' or 'Class' + bool isObjCARCBridgableType() const; + bool isCARCBridgableType() const; bool isTemplateTypeParmType() const; // C++ template type parameter bool isNullPtrType() const; // C++0x nullptr_t + /// Determines if this type, which must satisfy + /// isObjCLifetimeType(), is implicitly __unsafe_unretained rather + /// than implicitly __strong. + bool isObjCARCImplicitlyUnretainedType() const; + + /// Return the implicit lifetime for this type, which must not be dependent. + Qualifiers::ObjCLifetime getObjCARCImplicitLifetime() const; + enum ScalarTypeKind { STK_Pointer, STK_MemberPointer, @@ -1480,6 +1595,7 @@ public: } CanQualType getCanonicalTypeUnqualified() const; // in CanonicalType.h void dump() const; + static bool classof(const Type *) { return true; } friend class ASTReader; @@ -2340,30 +2456,33 @@ class FunctionType : public Type { // * AST read and write // * Codegen class ExtInfo { - // Feel free to rearrange or add bits, but if you go over 8, + // Feel free to rearrange or add bits, but if you go over 9, // you'll need to adjust both the Bits field below and // Type::FunctionTypeBitfields. - // | CC |noreturn|hasregparm|regparm - // |0 .. 2| 3 | 4 |5 .. 7 + // | CC |noreturn|produces|hasregparm|regparm + // |0 .. 2| 3 | 4 | 5 |6 .. 8 enum { CallConvMask = 0x7 }; enum { NoReturnMask = 0x8 }; - enum { HasRegParmMask = 0x10 }; - enum { RegParmMask = ~(CallConvMask | NoReturnMask), - RegParmOffset = 5 }; + enum { ProducesResultMask = 0x10 }; + enum { HasRegParmMask = 0x20 }; + enum { RegParmMask = ~(CallConvMask | NoReturnMask | ProducesResultMask), + RegParmOffset = 6 }; // Assumed to be the last field - unsigned char Bits; + uint16_t Bits; - ExtInfo(unsigned Bits) : Bits(static_cast(Bits)) {} + ExtInfo(unsigned Bits) : Bits(static_cast(Bits)) {} friend class FunctionType; public: // Constructor with no defaults. Use this when you know that you // have all the elements (when reading an AST file for example). - ExtInfo(bool noReturn, bool hasRegParm, unsigned regParm, CallingConv cc) { + ExtInfo(bool noReturn, bool hasRegParm, unsigned regParm, CallingConv cc, + bool producesResult) { Bits = ((unsigned) cc) | (noReturn ? NoReturnMask : 0) | + (producesResult ? ProducesResultMask : 0) | (hasRegParm ? HasRegParmMask : 0) | (regParm << RegParmOffset); } @@ -2373,6 +2492,7 @@ class FunctionType : public Type { ExtInfo() : Bits(0) {} bool getNoReturn() const { return Bits & NoReturnMask; } + bool getProducesResult() const { return Bits & ProducesResultMask; } bool getHasRegParm() const { return Bits & HasRegParmMask; } unsigned getRegParm() const { return Bits >> RegParmOffset; } CallingConv getCC() const { return CallingConv(Bits & CallConvMask); } @@ -2394,8 +2514,17 @@ class FunctionType : public Type { return ExtInfo(Bits & ~NoReturnMask); } + ExtInfo withProducesResult(bool producesResult) const { + if (producesResult) + return ExtInfo(Bits | ProducesResultMask); + else + return ExtInfo(Bits & ~ProducesResultMask); + } + ExtInfo withRegParm(unsigned RegParm) const { - return ExtInfo(HasRegParmMask | (Bits & ~RegParmMask) | (RegParm << RegParmOffset)); + return ExtInfo((Bits & ~RegParmMask) | + (HasRegParmMask) | + (RegParm << RegParmOffset)); } ExtInfo withCallingConv(CallingConv cc) const { @@ -2495,7 +2624,8 @@ public: struct ExtProtoInfo { ExtProtoInfo() : Variadic(false), ExceptionSpecType(EST_None), TypeQuals(0), - RefQualifier(RQ_None), NumExceptions(0), Exceptions(0), NoexceptExpr(0) {} + RefQualifier(RQ_None), NumExceptions(0), Exceptions(0), NoexceptExpr(0), + ConsumedArguments(0) {} FunctionType::ExtInfo ExtInfo; bool Variadic; @@ -2505,6 +2635,7 @@ public: unsigned NumExceptions; const QualType *Exceptions; Expr *NoexceptExpr; + const bool *ConsumedArguments; }; private: @@ -2523,7 +2654,7 @@ private: QualType canonical, const ExtProtoInfo &epi); /// NumArgs - The number of arguments this function has, not counting '...'. - unsigned NumArgs : 20; + unsigned NumArgs : 19; /// NumExceptions - The number of types in the exception spec, if any. unsigned NumExceptions : 9; @@ -2531,6 +2662,9 @@ private: /// ExceptionSpecType - The type of exception specification this function has. unsigned ExceptionSpecType : 3; + /// HasAnyConsumedArgs - Whether this function has any consumed arguments. + unsigned HasAnyConsumedArgs : 1; + /// ArgInfo - There is an variable size array after the class in memory that /// holds the argument types. @@ -2540,8 +2674,25 @@ private: /// NoexceptExpr - Instead of Exceptions, there may be a single Expr* pointing /// to the expression in the noexcept() specifier. + /// ConsumedArgs - A variable size array, following Exceptions + /// and of length NumArgs, holding flags indicating which arguments + /// are consumed. This only appears if HasAnyConsumedArgs is true. + friend class ASTContext; // ASTContext creates these. + const bool *getConsumedArgsBuffer() const { + assert(hasAnyConsumedArgs()); + + // Find the end of the exceptions. + Expr * const *eh_end = reinterpret_cast(arg_type_end()); + if (getExceptionSpecType() != EST_ComputedNoexcept) + eh_end += NumExceptions; + else + eh_end += 1; // NoexceptExpr + + return reinterpret_cast(eh_end); + } + public: unsigned getNumArgs() const { return NumArgs; } QualType getArgType(unsigned i) const { @@ -2562,6 +2713,8 @@ public: } else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) { EPI.NoexceptExpr = getNoexceptExpr(); } + if (hasAnyConsumedArgs()) + EPI.ConsumedArguments = getConsumedArgsBuffer(); return EPI; } @@ -2647,6 +2800,16 @@ public: return exception_begin() + NumExceptions; } + bool hasAnyConsumedArgs() const { + return HasAnyConsumedArgs; + } + bool isArgConsumed(unsigned I) const { + assert(I < getNumArgs() && "argument index out of range!"); + if (hasAnyConsumedArgs()) + return getConsumedArgsBuffer()[I]; + return false; + } + bool isSugared() const { return false; } QualType desugar() const { return QualType(this, 0); } @@ -2971,6 +3134,7 @@ public: // Enumerated operand (string or keyword). attr_objc_gc, + attr_objc_lifetime, attr_pcs, FirstEnumOperandKind = attr_objc_gc, diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index e4c6722e83..dea67ab9f3 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -400,6 +400,11 @@ def ObjCNSObject : InheritableAttr { let Spellings = ["NSObject"]; } +def ObjCPreciseLifetime : Attr { + let Spellings = ["objc_precise_lifetime"]; + let Subjects = [Var]; +} + def Overloadable : Attr { let Spellings = ["overloadable"]; } diff --git a/include/clang/Basic/DiagnosticCommonKinds.td b/include/clang/Basic/DiagnosticCommonKinds.td index 50110fb537..4b5de366cb 100644 --- a/include/clang/Basic/DiagnosticCommonKinds.td +++ b/include/clang/Basic/DiagnosticCommonKinds.td @@ -32,6 +32,7 @@ def note_type_being_defined : Note< def note_matching : Note<"to match this '%0'">; def note_using : Note<"using">; +def note_possibility : Note<"one possibility">; def note_also_found : Note<"also found">; // Parse && Lex diff --git a/include/clang/Basic/DiagnosticDriverKinds.td b/include/clang/Basic/DiagnosticDriverKinds.td index 908a69b162..409c6a2016 100644 --- a/include/clang/Basic/DiagnosticDriverKinds.td +++ b/include/clang/Basic/DiagnosticDriverKinds.td @@ -82,6 +82,10 @@ def err_drv_conflicting_deployment_targets : Error< "conflicting deployment targets, both '%0' and '%1' are present in environment">; def err_drv_invalid_arch_for_deployment_target : Error< "invalid architecture '%0' for deployment target '%1'">; +def err_drv_objc_gc_arr : Error< + "cannot specify both '-fobjc-arc' and '%0'">; +def err_arc_nonfragile_abi : Error< + "-fobjc-arc is not supported with fragile abi">; def warn_c_kext : Warning< "ignoring -fapple-kext which is valid for c++ and objective-c++ only">; diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index 4aa85134aa..120ba67dc1 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -137,6 +137,9 @@ def warn_pch_nonfragile_abi2 : Error< "PCH file was compiled with the %select{32-bit|enhanced non-fragile}0 " "Objective-C ABI but the %select{32-bit|enhanced non-fragile}1 " "Objective-C ABI is selected">; +def warn_pch_auto_ref_count : Error< + "PCH file was compiled %select{without|with} automated reference counting," + "which is currently %select{disabled|enabled}">; def warn_pch_apple_kext : Error< "PCH file was compiled %select{with|without}0 support for Apple's kernel " "extensions ABI but it is currently %select{disabled|enabled}1">; diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 98706dd227..7444ca44ad 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -169,6 +169,8 @@ def ImplicitAtomic : DiagGroup<"implicit-atomic-properties">; def CustomAtomic : DiagGroup<"custom-atomic-properties">; def AtomicProperties : DiagGroup<"atomic-properties", [ImplicitAtomic, CustomAtomic]>; +def AutomaticReferenceCountingABI : DiagGroup<"arc-abi">; +def AutomaticReferenceCounting : DiagGroup<"arc", [AutomaticReferenceCountingABI]>; def Selector : DiagGroup<"selector">; def NonfragileAbi2 : DiagGroup<"nonfragile-abi2">; def Protocol : DiagGroup<"protocol">; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index ea736c0cd2..00d0e6fab2 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -430,6 +430,12 @@ def warn_objc_property_copy_missing_on_block : Warning< def warn_atomic_property_rule : Warning< "writable atomic property %0 cannot pair a synthesized setter/getter " "with a user defined setter/getter">; +def warn_ownin_getter_rule : Warning< + "property's synthesized getter follows Cocoa naming" + " convention for returning 'owned' objects">; +def err_ownin_getter_rule : Error< + "property's synthesized getter follows Cocoa naming" + " convention for returning 'owned' objects">; def warn_default_atomic_custom_getter_setter : Warning< "atomic by default property %0 has a user defined %select{getter|setter}1 " "(property should be marked 'atomic' if this is intended)">, @@ -465,6 +471,8 @@ def error_bad_property_context : Error< def error_missing_property_ivar_decl : Error< "synthesized property %0 must either be named the same as a compatible" " ivar or must explicitly name an ivar">; +def error_synthesize_weak_non_arc_or_gc : Error< + "@synthesize of 'weak' property is only allowed in ARC or GC mode">; def error_synthesized_ivar_yet_not_supported : Error< "instance variable synthesis not yet supported" @@ -477,7 +485,7 @@ def error_ivar_in_superclass_use : Error< def error_weak_property : Error< "existing ivar %1 for __weak property %0 must be __weak">; def error_strong_property : Error< - "property %0 must be declared __weak to match existing ivar %1 with __weak attribute">; + "existing ivar %1 for strong property %0 may not be __weak">; def error_dynamic_property_ivar_decl : Error< "dynamic property can not have ivar specification">; def error_duplicate_ivar_use : Error< @@ -1138,6 +1146,8 @@ def err_format_attribute_implicit_this_format_string : Error< "format attribute cannot specify the implicit this argument as the format " "string">; def warn_unknown_method_family : Warning<"unrecognized method family">; +def err_init_method_bad_return_type : Error< + "init methods must return an object pointer type, not %0">; def err_attribute_invalid_size : Error< "vector size not an integral multiple of component size">; def err_attribute_zero_size : Error<"zero vector size">; @@ -1163,6 +1173,10 @@ def err_as_qualified_auto_decl : Error< "automatic variable qualified with an address space">; def err_arg_with_address_space : Error< "parameter may not be qualified with an address space">; +def err_attr_objc_lifetime_bad_type : Error< + "the type %0 cannot be retained">; +def err_attr_objc_lifetime_redundant : Error< + "the type %0 already has retainment attributes set on it">; def err_attribute_not_string : Error< "argument to %0 attribute was not a string literal">; def err_attribute_section_invalid_for_target : Error< @@ -1235,6 +1249,13 @@ def err_cconv_varargs : Error< def err_regparm_mismatch : Error<"function declared with with regparm(%0) " "attribute was previously declared " "%plural{0:without the regparm|:with the regparm(%1)}1 attribute">; +def err_objc_precise_lifetime_bad_type : Error< + "objc_precise_lifetime only applies to retainable types; type here is %0">; +def warn_objc_precise_lifetime_meaningless : Error< + "objc_precise_lifetime is not meaningful for " + "%select{__unsafe_unretained|__autoreleasing}0 objects">; +def warn_label_attribute_not_unused : Warning< + "The only valid attribute for labels is 'unused'">; def err_invalid_pcs : Error<"Invalid PCS type">; // Availability attribute @@ -1569,6 +1590,17 @@ def note_ovl_candidate_bad_gc : Note<"candidate " "constructor (inherited)}0%1 not viable: " "%select{%ordinal6|'this'}5 argument (%2) has %select{no|__weak|__strong}3 " "lifetime, but parameter has %select{no|__weak|__strong}4 lifetime">; +def note_ovl_candidate_bad_lifetime : Note<"candidate " + "%select{function|function|constructor|" + "function |function |constructor |" + "constructor (the implicit default constructor)|" + "constructor (the implicit copy constructor)|" + "function (the implicit copy assignment operator)|" + "constructor (inherited)}0%1 not viable: " + "%select{%ordinal6|'this'}5 argument (%2) has " + "%select{no|__unsafe_unretained|__strong|__weak|__autoreleasing}3 lifetime," + " but parameter has %select{no|__unsafe_unretained|__strong|__weak|" + "__autoreleasing}4 lifetime">; def note_ovl_candidate_bad_cvr_this : Note<"candidate " "%select{|function|||function||||" "function (the implicit copy assignment operator)|}0 not viable: " @@ -2202,8 +2234,8 @@ def err_unavailable_message : Error<"%0 is unavailable: %1">; def warn_unavailable_fwdclass_message : Warning< "%0 maybe unavailable because receiver type is unknown">; def note_unavailable_here : Note< - "function has been explicitly marked " - "%select{unavailable|deleted|deprecated}0 here">; + "%select{declaration|function}0 has been explicitly marked " + "%select{unavailable|deleted|deprecated}1 here">; def warn_not_enough_argument : Warning< "not enough variable arguments in %0 declaration to fit a sentinel">; def warn_missing_sentinel : Warning < @@ -2439,12 +2471,16 @@ def note_protected_by_objc_finally : Note< "jump bypasses initialization of @finally block">; def note_protected_by_objc_synchronized : Note< "jump bypasses initialization of @synchronized block">; +def note_protected_by_objc_autoreleasepool : Note< + "jump bypasses auto release push of @autoreleasepool block">; def note_protected_by_cxx_try : Note< "jump bypasses initialization of try block">; def note_protected_by_cxx_catch : Note< "jump bypasses initialization of catch block">; def note_protected_by___block : Note< "jump bypasses setup of __block variable">; +def note_protected_by_objc_lifetime : Note< + "jump bypasses initialization of retaining variable">; def note_exits_cleanup : Note< "jump exits scope of variable with __attribute__((cleanup))">; @@ -2464,6 +2500,10 @@ def note_exits_cxx_try : Note< "jump exits try block">; def note_exits_cxx_catch : Note< "jump exits catch block">; +def note_exits_objc_autoreleasepool : Note< + "jump exits autoreleasepool block">; +def note_exits_objc_lifetime : Note< + "jump exits scope of retaining variable">; def err_func_returning_array_function : Error< "function cannot return %select{array|function}0 type %1">; @@ -2495,6 +2535,139 @@ def ext_flexible_array_empty_aggregate_gnu : Extension< def ext_flexible_array_union_gnu : Extension< "flexible array member %0 in a union is a GNU extension">, InGroup; +let CategoryName = "Automatic Reference Counting Issue" in { + +// ARC-mode diagnostics. +def err_arc_weak_no_runtime : Error< + "the current deployment target does not support automated __weak references">; +def err_arc_illegal_explicit_message : Error< + "ARC forbids explicit message send of %0">; +def err_arc_unused_init_message : Error< + "the result of a delegate init call must be immediately returned " + "or assigned to 'self'">; +def err_arc_mismatched_cast : Error< + "%select{implicit conversion|cast}0 of " + "%select{%2|a non-Objective-C pointer type %2|a block pointer|" + "an Objective-C pointer|an indirect pointer to an Objective-C pointer}1" + " to %3 is disallowed with ARC">; +def err_arc_objc_object_in_struct : Error< + "ARC forbids Objective-C objects in structs or unions">; +def err_arc_objc_property_default_assign_on_object : Error< + "ARC forbids properties of Objective-C objects " + "with unspecified storage attribute">; +def err_arc_illegal_selector : Error< + "ARC forbids use of %0 in a @selector">; +def err_arc_illegal_method_def : Error< + "ARC forbids implementation of %0">; +def err_arc_lost_method_convention : Error< + "method was declared as %select{an 'alloc'|a 'copy'|an 'init'|a 'new'}0 " + "method, but its implementation doesn't match because %select{" + "its result type is not an object pointer|" + "its result type is unrelated to its receiver type}1">; +def note_arc_lost_method_convention : Note<"declaration in interface">; +def err_arc_gained_method_convention : Error< + "method implementation does not match its declaration">; +def note_arc_gained_method_convention : Note< + "declaration in interface is not in the '%select{alloc|copy|init|new}0' " + "family because %select{its result type is not an object pointer|" + "its result type is unrelated to its receiver type}1">; +def err_typecheck_arr_assign_self : Error< + "cannot assign to 'self' outside of a method in the init family">; +def err_typecheck_arr_assign_enumeration : Error< + "fast enumeration variables can't be modified in ARC by default; " + "declare the variable __strong to allow this">; +def warn_arc_non_pod_class_with_object_member : Warning< + "%0 cannot be shared between ARC and non-ARC " + "code; add a copy constructor, a copy assignment operator, and a destructor " + "to make it ABI-compatible">, InGroup, + DefaultIgnore; +def warn_arc_retained_assign : Warning< + "cannot assign retained object to %select{weak|unsafe_unretained}0 variable">; +def warn_arc_trivial_member_function_with_object_member : Warning< + "%0 cannot be shared between ARC and non-ARC " + "code; add a non-trivial %select{copy constructor|copy assignment operator|" + "destructor}1 to make it ABI-compatible">, + InGroup, DefaultIgnore; +def err_arc_new_array_without_lifetime : Error< + "'new' cannot allocate an array of %0 with no explicit lifetime">; +def warn_err_new_delete_object_array : Warning< + "%select{allocating|destroying}0 an array of %1; this array must not " + "%select{be deleted in|have been allocated from}0 non-ARC code">, + InGroup, DefaultIgnore; +def err_arc_autoreleasing_var : Error< + "%select{__block variables|global variables|fields|ivars}0 cannot have " + "__autoreleasing lifetime">; +def err_arc_thread_lifetime : Error< + "thread-local variable has non-trivial lifetime: type is %0">; +def err_arc_indirect_no_lifetime : Error< + "%select{pointer|reference}1 to non-const type %0 with no explicit lifetime">, + InGroup; +def err_arc_array_param_no_lifetime : Error< + "must explicitly describe intended lifetime of an object array parameter">; +def err_arc_pseudo_dtor_inconstant_quals : Error< + "pseudo-destructor destroys object of type %0 with inconsistently-qualified " + "type %1">; +def err_arc_init_method_unrelated_result_type : Error< + "init methods must return a type related to the receiver type">; +def err_arc_nonlocal_writeback : Error< + "passing address of %select{non-local|non-scalar}0 object to " + "__autoreleasing parameter for write-back">; +def err_arc_method_not_found : Error< + "no known %select{instance|class}1 method for selector %0">; +def err_arc_receiver_forward_class : Error< + "receiver %0 for class message is a forward declaration">; +def err_arc_may_not_respond : Error< + "receiver type %0 for instance message does not declare a method with " + "selector %1">; +def err_arc_receiver_forward_instance : Error< + "receiver type %0 for instance message is a forward declaration">; +def err_arc_multiple_method_decl : Error< + "multiple methods named %0 found with mismatched result, " + "parameter type or attributes">; +def warn_arc_retain_cycle : Warning< + "capturing %0 strongly in this block is likely to lead to a retain cycle">, + InGroup>; +def note_arc_retain_cycle_owner : Note< + "block will be retained by %select{the captured object|an object strongly " + "retained by the captured object}0">; +def note_nontrivial_objc_lifetime : Note< + "because type %0 has %select{no|no|__strong|__weak|__autoreleasing}1 " + "lifetime">; +def warn_arc_object_memaccess : Warning< + "%select{destination for|source of}0 this %1 call is a pointer to " + "lifetime-qualified type %2">, InGroup>; + +def err_arc_strong_property_lifetime : Error< + "existing ivar %1 for strong property %0 may not be " + "%select{|__unsafe_unretained||__weak}2">; +def err_arc_assign_property_lifetime : Error< + "existing ivar %1 for unsafe_unretained property %0 must be __unsafe_unretained">; +def err_arc_inconsistent_property_lifetime : Error< + "%select{strong|weak|unsafe_unretained}1 property %0 may not also be " + "declared %select{|__unsafe_unretained|__strong|__weak|__autoreleasing}2">; +def err_arc_atomic_lifetime : Error< + "cannot perform atomic operation on a pointer to type %0: type has " + "non-trivial lifetime">; + +def err_arc_bridge_cast_incompatible : Error< + "incompatible types casting %0 to %1 with a %select{__bridge|" + "__bridge_transfer|__bridge_retained}2 cast">; +def err_arc_bridge_cast_wrong_kind : Error< + "cast of %select{Objective-C|block|C}0 pointer type %1 to " + "%select{Objective-C|block|C}2 pointer type %3 cannot use %select{__bridge|" + "__bridge_transfer|__bridge_retained}4">; +def err_arc_cast_requires_bridge : Error< + "cast of %select{Objective-C|block|C}0 pointer type %1 to " + "%select{Objective-C|block|C}2 pointer type %3 requires a bridged cast">; +def note_arc_bridge : Note< + "use __bridge to convert directly (no change in ownership)">; +def note_arc_bridge_transfer : Note< + "use __bridge_transfer to transfer ownership of a +1 %0 into ARC">; +def note_arc_bridge_retained : Note< + "use __bridge_retained to make an ARC object available as a +1 %0">; + +} // ARC category name + def err_flexible_array_init_needs_braces : Error< "flexible array requires brace-enclosed initializer">; def err_illegal_decl_array_of_functions : Error< @@ -3337,6 +3510,15 @@ def err_typecheck_incompatible_address_space : Error< "|sending %0 to parameter of type %1" "|casting %0 to type %1}2" " changes address space of pointer">; +def err_typecheck_incompatible_lifetime : Error< + "%select{assigning %1 to %0" + "|passing %0 to parameter of type %1" + "|returning %0 from a function with result type %1" + "|converting %0 to type %1" + "|initializing %0 with an expression of type %1" + "|sending %0 to parameter of type %1" + "|casting %0 to type %1}2" + " changes retain/release properties of pointer">; def err_typecheck_convert_ambiguous : Error< "ambiguity in initializing value of type %0 with initializer of type %1">; def err_typecheck_comparison_of_distinct_blocks : Error< diff --git a/include/clang/Basic/LangOptions.h b/include/clang/Basic/LangOptions.h index 0557b41f51..7eda0aa3fd 100644 --- a/include/clang/Basic/LangOptions.h +++ b/include/clang/Basic/LangOptions.h @@ -130,6 +130,10 @@ public: unsigned DefaultFPContract : 1; // Default setting for FP_CONTRACT // FIXME: This is just a temporary option, for testing purposes. unsigned NoBitFieldTypeAlign : 1; + unsigned ObjCAutoRefCount : 1; // Objective C automated reference counting + unsigned ObjCNoAutoRefCountRuntime : 1; // ARC w/o extra runtime support + unsigned ObjCInferRelatedReturnType : 1; // Infer Objective-C related return + // types unsigned FakeAddressSpaceMap : 1; // Use a fake address space map, for // testing languages such as OpenCL. @@ -172,6 +176,8 @@ public: Trigraphs = BCPLComment = Bool = DollarIdents = AsmPreprocessor = 0; GNUMode = GNUKeywords = ImplicitInt = Digraphs = 0; HexFloats = 0; + ObjCAutoRefCount = ObjCNoAutoRefCountRuntime = 0; + ObjCInferRelatedReturnType = 0; GC = ObjC1 = ObjC2 = ObjCNonFragileABI = ObjCNonFragileABI2 = 0; AppleKext = 0; ObjCDefaultSynthProperties = 0; diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td index 03f4cc3ec6..f503950c20 100644 --- a/include/clang/Basic/StmtNodes.td +++ b/include/clang/Basic/StmtNodes.td @@ -37,6 +37,7 @@ def ObjCAtFinallyStmt : Stmt; def ObjCAtThrowStmt : Stmt; def ObjCAtSynchronizedStmt : Stmt; def ObjCForCollectionStmt : Stmt; +def ObjCAutoreleasePoolStmt : Stmt; // C++ statments def CXXCatchStmt : Stmt; @@ -130,6 +131,10 @@ def ObjCProtocolExpr : DStmt; def ObjCIvarRefExpr : DStmt; def ObjCPropertyRefExpr : DStmt; def ObjCIsaExpr : DStmt; +def ObjCIndirectCopyRestoreExpr : DStmt; + +// Obj-C ARC Expressions. +def ObjCBridgedCastExpr : DStmt; // CUDA Expressions. def CUDAKernelCallExpr : DStmt; diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index dfba7eec8a..dd105a169b 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -422,6 +422,11 @@ KEYWORD(__pascal , KEYALL) KEYWORD(__vector , KEYALTIVEC) KEYWORD(__pixel , KEYALTIVEC) +// Objective-C ARC keywords. +KEYWORD(__bridge , KEYARC) +KEYWORD(__bridge_transfer , KEYARC) +KEYWORD(__bridge_retained , KEYARC) + // Alternate spelling for various tokens. There are GCC extensions in all // languages, but should not be disabled in strict conformance mode. ALIAS("__alignof__" , __alignof , KEYALL) @@ -507,6 +512,7 @@ OBJC1_AT_KEYWORD(try) OBJC1_AT_KEYWORD(catch) OBJC1_AT_KEYWORD(finally) OBJC1_AT_KEYWORD(synchronized) +OBJC1_AT_KEYWORD(autoreleasepool) OBJC2_AT_KEYWORD(property) OBJC2_AT_KEYWORD(package) diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index e4ec205817..cc594085ff 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -383,6 +383,13 @@ def create_module : Flag<"-create-module">, HelpText<"Create a module definition file">; } +def arcmt_check : Flag<"-arcmt-check">, + HelpText<"Check for ARC migration issues that need manual handling">; +def arcmt_modify : Flag<"-arcmt-modify">, + HelpText<"Apply modifications to files to conform to ARC">; +def arcmt_modify_in_memory : Flag<"-arcmt-modify-in-memory">, + HelpText<"Apply ARC conforming modifications & compile using memory buffers">; + def import_module : Separate<"-import-module">, HelpText<"Import a module definition file">; @@ -479,6 +486,14 @@ def fconstant_string_class : Separate<"-fconstant-string-class">, HelpText<"Specify the class to use for constant Objective-C string objects.">; def fno_constant_cfstrings : Flag<"-fno-constant-cfstrings">, HelpText<"Enable creation of CodeFoundation-type constant strings">; +def fobjc_arc : Flag<"-fobjc-arc">, + HelpText<"Synthesize retain and release calls for Objective-C pointers">; +def fobjc_arc_cxxlib_EQ : Joined<"-fobjc-arc-cxxlib=">, + HelpText<"Objective-C++ Automatic Reference Counting standard library kind">; +def fobjc_no_arc_runtime : Flag<"-fobjc-no-arc-runtime">, + HelpText<"Implement -fobjc-arc without any extra runtime support">; +def fobjc_arc_exceptions : Flag<"-fobjc-arc-exceptions">, + HelpText<"Use EH-safe code when synthesizing retains and releases in -fobjc-arc">; def fobjc_gc : Flag<"-fobjc-gc">, HelpText<"Enable Objective-C garbage collection">; def fobjc_gc_only : Flag<"-fobjc-gc-only">, diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 23b351c78a..079f759514 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -112,6 +112,13 @@ def ccc_print_phases : Flag<"-ccc-print-phases">, CCCDebugOpt, def ccc_print_bindings : Flag<"-ccc-print-bindings">, CCCDebugOpt, HelpText<"Show bindings of tools to actions">; +def ccc_arrmt_check : Flag<"-ccc-arrmt-check">, CCCDriverOpt, + HelpText<"Check for ARC migration issues that need manual handling">; +def ccc_arrmt_modify : Flag<"-ccc-arrmt-modify">, CCCDriverOpt, + HelpText<"Apply modifications to files to conform to ARC">; +def ccc_arrmt_modify_in_memory : Flag<"-ccc-arrmt-modify-in-memory">, + HelpText<"Apply ARC conforming modifications & compile using memory buffers">; + // Make sure all other -ccc- options are rejected. def ccc_ : Joined<"-ccc-">, Group, Flags<[Unsupported]>; @@ -382,6 +389,10 @@ def fno_verbose_asm : Flag<"-fno-verbose-asm">, Group; def fno_working_directory : Flag<"-fno-working-directory">, Group; def fno_wrapv : Flag<"-fno-wrapv">, Group; def fno_zero_initialized_in_bss : Flag<"-fno-zero-initialized-in-bss">, Group; +def fobjc_arc : Flag<"-fobjc-arc">, Group; +def fno_objc_arc : Flag<"-fno-objc-arc">, Group; +def fobjc_arc_exceptions : Flag<"-fobjc-arc-exceptions">, Group; +def fno_objc_arc_exceptions : Flag<"-fno-objc-arc-exceptions">, Group; def fobjc_atdefs : Flag<"-fobjc-atdefs">, Group; def fobjc_call_cxx_cdtors : Flag<"-fobjc-call-cxx-cdtors">, Group; def fobjc_default_synthesize_properties : @@ -392,6 +403,10 @@ def fobjc_gc_only : Flag<"-fobjc-gc-only">, Group; def fobjc_gc : Flag<"-fobjc-gc">, Group; def fobjc_legacy_dispatch : Flag<"-fobjc-legacy-dispatch">, Group; def fobjc_new_property : Flag<"-fobjc-new-property">, Group; +def fobjc_infer_related_result_type : Flag<"-fobjc-infer-related-result-type">, + Group; +def fno_objc_infer_related_result_type : Flag< + "-fno-objc-infer-related-result-type">, Group; // Objective-C ABI options. def fobjc_abi_version_EQ : Joined<"-fobjc-abi-version=">, Group; diff --git a/include/clang/Driver/ToolChain.h b/include/clang/Driver/ToolChain.h index 626d54c37b..053732a82f 100644 --- a/include/clang/Driver/ToolChain.h +++ b/include/clang/Driver/ToolChain.h @@ -166,6 +166,9 @@ public: /// UseSjLjExceptions - Does this tool chain use SjLj exceptions. virtual bool UseSjLjExceptions() const { return false; } + /// HasARCRuntime - Does this tool chain provide a specialized ARC runtime. + virtual bool HasARCRuntime() const { return false; } + /// ComputeLLVMTriple - Return the LLVM target triple to use, after taking /// command line arguments into account. virtual std::string ComputeLLVMTriple(const ArgList &Args) const; @@ -184,7 +187,8 @@ public: /// AddClangCXXStdlibIncludeArgs - Add the clang -cc1 level arguments to set /// the include paths to use for the given C++ standard library type. virtual void AddClangCXXStdlibIncludeArgs(const ArgList &Args, - ArgStringList &CmdArgs) const; + ArgStringList &CmdArgs, + bool ObjCXXAutoRefCount) const; /// AddCXXStdlibLibArgs - Add the system specific linker arguments to use /// for the given C++ standard library type. diff --git a/include/clang/Frontend/CodeGenOptions.h b/include/clang/Frontend/CodeGenOptions.h index 1c686c76df..7736d7fd6a 100644 --- a/include/clang/Frontend/CodeGenOptions.h +++ b/include/clang/Frontend/CodeGenOptions.h @@ -36,6 +36,7 @@ public: }; unsigned AsmVerbose : 1; /// -dA, -fverbose-asm. + unsigned ObjCAutoRefCountExceptions : 1; /// Whether ARC should be EH-safe. unsigned CXAAtExit : 1; /// Use __cxa_atexit for calling destructors. unsigned CXXCtorDtorAliases: 1; /// Emit complete ctors/dtors as linker /// aliases to base ctors when possible. @@ -133,6 +134,7 @@ public: public: CodeGenOptions() { AsmVerbose = 0; + ObjCAutoRefCountExceptions = 0; CXAAtExit = 1; CXXCtorDtorAliases = 0; DataSections = 0; diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h index 02f6f868fe..e97be26a64 100644 --- a/include/clang/Frontend/FrontendOptions.h +++ b/include/clang/Frontend/FrontendOptions.h @@ -77,6 +77,13 @@ public: unsigned FixWhatYouCan : 1; ///< Apply fixes even if there are /// unfixable errors. + enum { + ARCMT_None, + ARCMT_Check, + ARCMT_Modify, + ARCMT_ModifyInMemory + } ARCMTAction; + /// The input files and their types. std::vector > Inputs; @@ -131,6 +138,7 @@ public: ShowStats = 0; ShowTimers = 0; ShowVersion = 0; + ARCMTAction = ARCMT_None; } /// getInputKindForExtension - Return the appropriate input kind for a file diff --git a/include/clang/Frontend/PreprocessorOptions.h b/include/clang/Frontend/PreprocessorOptions.h index e471c5cf1d..2f99aaa522 100644 --- a/include/clang/Frontend/PreprocessorOptions.h +++ b/include/clang/Frontend/PreprocessorOptions.h @@ -26,6 +26,15 @@ namespace clang { class Preprocessor; class LangOptions; +/// \brief Enumerate the kinds of standard library that +enum ObjCXXARCStandardLibraryKind { + ARCXX_nolib, + /// \brief libc++ + ARCXX_libcxx, + /// \brief libstdc++ + ARCXX_libstdcxx +}; + /// PreprocessorOptions - This class is used for passing the various options /// used in preprocessor initialization to InitializePreprocessor(). class PreprocessorOptions { @@ -104,6 +113,11 @@ public: /// compiler invocation and its buffers will be reused. bool RetainRemappedFileBuffers; + /// \brief The Objective-C++ ARC standard library that we should support, + /// by providing appropriate definitions to retrofit the standard library + /// with support for lifetime-qualified pointers. + ObjCXXARCStandardLibraryKind ObjCXXARCStandardLibrary; + typedef std::vector >::iterator remapped_file_iterator; typedef std::vector >::const_iterator @@ -145,7 +159,8 @@ public: DumpDeserializedPCHDecls(false), PrecompiledPreambleBytes(0, true), RemappedFilesKeepOriginalName(true), - RetainRemappedFileBuffers(false) { } + RetainRemappedFileBuffers(false), + ObjCXXARCStandardLibrary(ARCXX_nolib) { } void addMacroDef(llvm::StringRef Name) { Macros.push_back(std::make_pair(Name, false)); diff --git a/include/clang/Frontend/Utils.h b/include/clang/Frontend/Utils.h index 3c34c2dce2..93d2c7d501 100644 --- a/include/clang/Frontend/Utils.h +++ b/include/clang/Frontend/Utils.h @@ -19,6 +19,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Support/raw_ostream.h" +#include "clang/Basic/Diagnostic.h" namespace llvm { class Triple; diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index e4cdc27c43..cb8df4c938 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1375,6 +1375,7 @@ bool ParseAsmOperandsOpt(llvm::SmallVectorImpl &Names, StmtResult ParseObjCTryStmt(SourceLocation atLoc); StmtResult ParseObjCThrowStmt(SourceLocation atLoc); StmtResult ParseObjCSynchronizedStmt(SourceLocation atLoc); + StmtResult ParseObjCAutoreleasePoolStmt(SourceLocation atLoc); //===--------------------------------------------------------------------===// @@ -1616,7 +1617,8 @@ bool ParseAsmOperandsOpt(llvm::SmallVectorImpl &Names, TypeResult ParseTypeName(SourceRange *Range = 0, Declarator::TheContext Context - = Declarator::TypeNameContext); + = Declarator::TypeNameContext, + ObjCDeclSpec *objcQuals = 0); void ParseBlockId(); void ProhibitAttributes(ParsedAttributesWithRange &attrs) { diff --git a/include/clang/Rewrite/Rewriter.h b/include/clang/Rewrite/Rewriter.h index 7861e99928..676744ada3 100644 --- a/include/clang/Rewrite/Rewriter.h +++ b/include/clang/Rewrite/Rewriter.h @@ -183,8 +183,11 @@ public: /// InsertText - Insert the specified string at the specified location in the /// original buffer. This method returns true (and does nothing) if the input /// location was not rewritable, false otherwise. + /// + /// \param indentNewLines if true new lines in the string are indented + /// using the indentation of the source line in position \arg Loc. bool InsertText(SourceLocation Loc, llvm::StringRef Str, - bool InsertAfter = true); + bool InsertAfter = true, bool indentNewLines = false); /// InsertTextAfter - Insert the specified string at the specified location in /// the original buffer. This method returns true (and does nothing) if diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index 72cd47589f..a3122db3b3 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -198,6 +198,7 @@ public: AT_objc_method_family, AT_cf_returns_not_retained, // Clang-specific. AT_cf_returns_retained, // Clang-specific. + AT_cf_returns_autoreleased, // Clang-specific. AT_ns_returns_not_retained, // Clang-specific. AT_ns_returns_retained, // Clang-specific. AT_ns_returns_autoreleased, // Clang-specific. @@ -205,6 +206,8 @@ public: AT_ns_consumed, // Clang-specific. AT_ns_consumes_self, // Clang-specific. AT_objc_gc, + AT_objc_lifetime, // Clang-specific. + AT_objc_precise_lifetime, // Clang-specific. AT_opencl_image_access, // OpenCL-specific. AT_opencl_kernel_function, // OpenCL-specific. AT_overloadable, // Clang-specific. diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index 7ce4e00943..e0faab9426 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -40,6 +40,7 @@ namespace clang { class NamespaceDecl; class NestedNameSpecifier; class NestedNameSpecifierLoc; + class ObjCDeclSpec; class Preprocessor; class Declarator; struct TemplateIdAnnotation; @@ -344,6 +345,8 @@ private: void SaveWrittenBuiltinSpecs(); void SaveStorageSpecifierAsWritten(); + ObjCDeclSpec *ObjCQualifiers; + static bool isTypeRep(TST T) { return (T == TST_typename || T == TST_typeofType || T == TST_underlyingType); @@ -383,7 +386,8 @@ public: ProtocolQualifiers(0), NumProtocolQualifiers(0), ProtocolLocs(0), - writtenBS() { + writtenBS(), + ObjCQualifiers(0) { } ~DeclSpec() { delete [] ProtocolQualifiers; @@ -653,6 +657,9 @@ public: return writtenBS; } + ObjCDeclSpec *getObjCQualifiers() const { return ObjCQualifiers; } + void setObjCQualifiers(ObjCDeclSpec *quals) { ObjCQualifiers = quals; } + /// isMissingDeclaratorOk - This checks if this DeclSpec can stand alone, /// without a Declarator. Only tag declspecs can stand alone. bool isMissingDeclaratorOk(); @@ -689,7 +696,10 @@ public: DQ_PR_copy = 0x20, DQ_PR_nonatomic = 0x40, DQ_PR_setter = 0x80, - DQ_PR_atomic = 0x100 + DQ_PR_atomic = 0x100, + DQ_PR_weak = 0x200, + DQ_PR_strong = 0x400, + DQ_PR_unsafe_unretained = 0x800 }; @@ -723,7 +733,7 @@ private: ObjCDeclQualifier objcDeclQualifier : 6; // NOTE: VC++ treats enums as signed, avoid using ObjCPropertyAttributeKind - unsigned PropertyAttributes : 9; + unsigned PropertyAttributes : 12; IdentifierInfo *GetterName; // getter name of NULL if no getter IdentifierInfo *SetterName; // setter name of NULL if no setter }; diff --git a/include/clang/Sema/DelayedDiagnostic.h b/include/clang/Sema/DelayedDiagnostic.h index 8395138ab6..8ab9382264 100644 --- a/include/clang/Sema/DelayedDiagnostic.h +++ b/include/clang/Sema/DelayedDiagnostic.h @@ -112,7 +112,7 @@ private: /// the complete parsing of the current declaration. class DelayedDiagnostic { public: - enum DDKind { Deprecation, Access }; + enum DDKind { Deprecation, Access, ForbiddenType }; unsigned char Kind; // actually a DDKind bool Triggered; @@ -135,6 +135,20 @@ public: return DD; } + static DelayedDiagnostic makeForbiddenType(SourceLocation loc, + unsigned diagnostic, + QualType type, + unsigned argument) { + DelayedDiagnostic DD; + DD.Kind = ForbiddenType; + DD.Triggered = false; + DD.Loc = loc; + DD.ForbiddenTypeData.Diagnostic = diagnostic; + DD.ForbiddenTypeData.OperandType = type.getAsOpaquePtr(); + DD.ForbiddenTypeData.Argument = argument; + return DD; + } + AccessedEntity &getAccessData() { assert(Kind == Access && "Not an access diagnostic."); return *reinterpret_cast(AccessData); @@ -155,6 +169,25 @@ public: DeprecationData.MessageLen); } + /// The diagnostic ID to emit. Used like so: + /// Diag(diag.Loc, diag.getForbiddenTypeDiagnostic()) + /// << diag.getForbiddenTypeOperand() + /// << diag.getForbiddenTypeArgument(); + unsigned getForbiddenTypeDiagnostic() const { + assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); + return ForbiddenTypeData.Diagnostic; + } + + unsigned getForbiddenTypeArgument() const { + assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); + return ForbiddenTypeData.Argument; + } + + QualType getForbiddenTypeOperand() const { + assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); + return QualType::getFromOpaquePtr(ForbiddenTypeData.OperandType); + } + private: union { /// Deprecation. @@ -164,6 +197,12 @@ private: size_t MessageLen; } DeprecationData; + struct { + unsigned Diagnostic; + unsigned Argument; + void *OperandType; + } ForbiddenTypeData; + /// Access control. char AccessData[sizeof(AccessedEntity)]; }; diff --git a/include/clang/Sema/Initialization.h b/include/clang/Sema/Initialization.h index 5dc4438671..7a5dbaca19 100644 --- a/include/clang/Sema/Initialization.h +++ b/include/clang/Sema/Initialization.h @@ -86,9 +86,13 @@ private: QualType Type; union { - /// \brief When Kind == EK_Variable, EK_Parameter, or EK_Member, - /// the VarDecl, ParmVarDecl, or FieldDecl, respectively. + /// \brief When Kind == EK_Variable or EK_Member, the VarDecl or + /// FieldDecl, respectively. DeclaratorDecl *VariableOrMember; + + /// \brief When Kind == EK_Parameter, the ParmVarDecl, with the + /// low bit indicating whether the parameter is "consumed". + uintptr_t Parameter; /// \brief When Kind == EK_Temporary, the type source information for /// the temporary. @@ -123,11 +127,6 @@ private: : Kind(EK_Variable), Parent(0), Type(Var->getType()), VariableOrMember(Var) { } - /// \brief Create the initialization entity for a parameter. - InitializedEntity(ParmVarDecl *Parm) - : Kind(EK_Parameter), Parent(0), Type(Parm->getType().getUnqualifiedType()), - VariableOrMember(Parm) { } - /// \brief Create the initialization entity for the result of a /// function, throwing an object, performing an explicit cast, or /// initializing a parameter for which there is no declaration. @@ -157,20 +156,28 @@ public: /// \brief Create the initialization entity for a parameter. static InitializedEntity InitializeParameter(ASTContext &Context, ParmVarDecl *Parm) { - InitializedEntity Res(Parm); - Res.Type = Context.getVariableArrayDecayedType(Res.Type); - return Res; + bool Consumed = (Context.getLangOptions().ObjCAutoRefCount && + Parm->hasAttr()); + + InitializedEntity Entity; + Entity.Kind = EK_Parameter; + Entity.Type = Context.getVariableArrayDecayedType( + Parm->getType().getUnqualifiedType()); + Entity.Parent = 0; + Entity.Parameter = (Consumed | reinterpret_cast(Parm)); + return Entity; } /// \brief Create the initialization entity for a parameter that is /// only known by its type. static InitializedEntity InitializeParameter(ASTContext &Context, - QualType Type) { + QualType Type, + bool Consumed) { InitializedEntity Entity; Entity.Kind = EK_Parameter; Entity.Type = Context.getVariableArrayDecayedType(Type); Entity.Parent = 0; - Entity.VariableOrMember = 0; + Entity.Parameter = (Consumed); return Entity; } @@ -268,6 +275,13 @@ public: /// \brief Determine whether this initialization allows the named return /// value optimization, which also applies to thrown objects. bool allowsNRVO() const; + + /// \brief Determine whether this initialization consumes the + /// parameter. + bool isParameterConsumed() const { + assert(getKind() == EK_Parameter && "Not a parameter"); + return (Parameter & 1); + } /// \brief Retrieve the base specifier. CXXBaseSpecifier *getBaseSpecifier() const { @@ -287,7 +301,7 @@ public: assert(getKind() == EK_Result && "No 'return' location!"); return SourceLocation::getFromRawEncoding(LocAndNRVO.Location); } - + /// \brief Determine the location of the 'throw' keyword when initializing /// an exception object. SourceLocation getThrowLoc() const { @@ -325,8 +339,10 @@ private: SIK_Value = IK_Value, ///< Value initialization SIK_ImplicitValue, ///< Implicit value initialization SIK_DirectCast, ///< Direct initialization due to a cast - /// \brief Direct initialization due to a C-style or functional cast. - SIK_DirectCStyleOrFunctionalCast + /// \brief Direct initialization due to a C-style cast. + SIK_DirectCStyleCast, + /// \brief Direct initialization due to a functional-style cast. + SIK_DirectFunctionalCast }; /// \brief The kind of initialization being performed. @@ -352,15 +368,29 @@ public: return InitializationKind(SIK_Direct, InitLoc, LParenLoc, RParenLoc); } - /// \brief Create a direct initialization due to a cast. - static InitializationKind CreateCast(SourceRange TypeRange, - bool IsCStyleCast) { - return InitializationKind(IsCStyleCast? SIK_DirectCStyleOrFunctionalCast - : SIK_DirectCast, + /// \brief Create a direct initialization due to a cast that isn't a C-style + /// or functional cast. + static InitializationKind CreateCast(SourceRange TypeRange) { + return InitializationKind(SIK_DirectCast, TypeRange.getBegin(), TypeRange.getBegin(), TypeRange.getEnd()); } + /// \brief Create a direct initialization for a C-style cast. + static InitializationKind CreateCStyleCast(SourceLocation StartLoc, + SourceRange TypeRange) { + return InitializationKind(SIK_DirectCStyleCast, + StartLoc, TypeRange.getBegin(), + TypeRange.getEnd()); + } + + /// \brief Create a direct initialization for a functional cast. + static InitializationKind CreateFunctionalCast(SourceRange TypeRange) { + return InitializationKind(SIK_DirectFunctionalCast, + TypeRange.getBegin(), TypeRange.getBegin(), + TypeRange.getEnd()); + } + /// \brief Create a copy initialization. static InitializationKind CreateCopy(SourceLocation InitLoc, SourceLocation EqualLoc) { @@ -393,12 +423,24 @@ public: /// \brief Determine whether this initialization is an explicit cast. bool isExplicitCast() const { - return Kind == SIK_DirectCast || Kind == SIK_DirectCStyleOrFunctionalCast; + return Kind == SIK_DirectCast || + Kind == SIK_DirectCStyleCast || + Kind == SIK_DirectFunctionalCast; } /// \brief Determine whether this initialization is a C-style cast. bool isCStyleOrFunctionalCast() const { - return Kind == SIK_DirectCStyleOrFunctionalCast; + return Kind == SIK_DirectCStyleCast || Kind == SIK_DirectFunctionalCast; + } + + /// brief Determine whether this is a C-style cast. + bool isCStyleCast() const { + return Kind == SIK_DirectCStyleCast; + } + + /// brief Determine whether this is a functional-style cast. + bool isFunctionalCast() const { + return Kind == SIK_DirectFunctionalCast; } /// \brief Determine whether this initialization is an implicit @@ -500,7 +542,13 @@ public: SK_ObjCObjectConversion, /// \brief Array initialization (from an array rvalue). /// This is a GNU C extension. - SK_ArrayInit + SK_ArrayInit, + /// \brief Pass an object by indirect copy-and-restore. + SK_PassByIndirectCopyRestore, + /// \brief Pass an object by indirect restore. + SK_PassByIndirectRestore, + /// \brief Produce an Objective-C object pointer. + SK_ProduceObjCObject }; /// \brief A single step in the initialization sequence. @@ -774,6 +822,13 @@ public: /// \brief Add an array initialization step. void AddArrayInitStep(QualType T); + /// \brief Add a step to pass an object by indirect copy-restore. + void AddPassByIndirectCopyRestoreStep(QualType T, bool shouldCopy); + + /// \brief Add a step to "produce" an Objective-C object (by + /// retaining it). + void AddProduceObjCObjectStep(QualType T); + /// \brief Note that this initialization sequence failed. void SetFailed(FailureKind Failure) { SequenceKind = FailedSequence; diff --git a/include/clang/Sema/Overload.h b/include/clang/Sema/Overload.h index 55931f2318..32d4cbdac1 100644 --- a/include/clang/Sema/Overload.h +++ b/include/clang/Sema/Overload.h @@ -77,6 +77,7 @@ namespace clang { ICK_Complex_Real, ///< Complex-real conversions (C99 6.3.1.7) ICK_Block_Pointer_Conversion, ///< Block Pointer conversions ICK_TransparentUnionConversion, /// Transparent Union Conversions + ICK_Writeback_Conversion, ///< Objective-C ARC writeback conversion ICK_Num_Conversion_Kinds ///< The number of conversion kinds }; @@ -100,10 +101,11 @@ namespace clang { /// 13.3.3.1.1) and are listed such that better conversion ranks /// have smaller values. enum ImplicitConversionRank { - ICR_Exact_Match = 0, ///< Exact Match - ICR_Promotion, ///< Promotion - ICR_Conversion, ///< Conversion - ICR_Complex_Real_Conversion ///< Complex <-> Real conversion + ICR_Exact_Match = 0, ///< Exact Match + ICR_Promotion, ///< Promotion + ICR_Conversion, ///< Conversion + ICR_Complex_Real_Conversion, ///< Complex <-> Real conversion + ICR_Writeback_Conversion ///< ObjC ARC writeback conversion }; ImplicitConversionRank GetConversionRank(ImplicitConversionKind Kind); @@ -137,6 +139,10 @@ namespace clang { /// (C++ 4.2p2). unsigned DeprecatedStringLiteralToCharPtr : 1; + /// \brief Whether the qualification conversion involves a change in the + /// Objective-C lifetime (for automatic reference counting). + unsigned QualificationIncludesObjCLifetime : 1; + /// IncompatibleObjC - Whether this is an Objective-C conversion /// that we should warn about (if we actually use it). unsigned IncompatibleObjC : 1; @@ -163,6 +169,10 @@ namespace clang { /// non-static member function without a ref-qualifier. unsigned BindsImplicitObjectArgumentWithoutRefQualifier : 1; + /// \brief Whether this binds a reference to an object with a different + /// Objective-C lifetime qualifier. + unsigned ObjCLifetimeConversionBinding : 1; + /// FromType - The type that this conversion is converting /// from. This is an opaque pointer that can be translated into a /// QualType. diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 4ddc80a72d..2ec868a94d 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -108,6 +108,7 @@ namespace clang { class ObjCInterfaceDecl; class ObjCIvarDecl; template class ObjCList; + class ObjCMessageExpr; class ObjCMethodDecl; class ObjCPropertyDecl; class ObjCProtocolDecl; @@ -247,6 +248,10 @@ public: /// VisContext - Manages the stack for #pragma GCC visibility. void *VisContext; // Really a "PragmaVisStack*" + /// ExprNeedsCleanups - True if the current evaluation context + /// requires cleanups to be run at its conclusion. + bool ExprNeedsCleanups; + /// \brief Stack containing information about each of the nested /// function, block, and method scopes that are currently active. /// @@ -555,6 +560,9 @@ public: /// \brief The expression evaluation context. ExpressionEvaluationContext Context; + /// \brief Whether the enclosing context needed a cleanup. + bool ParentNeedsCleanups; + /// \brief The number of temporaries that were active when we /// entered this expression evaluation context. unsigned NumTemporaries; @@ -573,8 +581,10 @@ public: PotentiallyEmittedDiagnostics *PotentiallyDiagnosed; ExpressionEvaluationContextRecord(ExpressionEvaluationContext Context, - unsigned NumTemporaries) - : Context(Context), NumTemporaries(NumTemporaries), + unsigned NumTemporaries, + bool ParentNeedsCleanups) + : Context(Context), ParentNeedsCleanups(ParentNeedsCleanups), + NumTemporaries(NumTemporaries), PotentiallyReferenced(0), PotentiallyDiagnosed(0) { } void addReferencedDecl(SourceLocation Loc, Decl *Decl) { @@ -766,7 +776,7 @@ public: return FunctionScopes.back(); } - bool hasAnyErrorsInThisFunction() const; + bool hasAnyUnrecoverableErrorsInThisFunction() const; /// \brief Retrieve the current block, if any. sema::BlockScopeInfo *getCurBlock(); @@ -838,7 +848,7 @@ public: const FunctionProtoType *Source, SourceLocation SourceLoc); TypeResult ActOnTypeName(Scope *S, Declarator &D); - + bool RequireCompleteType(SourceLocation Loc, QualType T, const PartialDiagnostic &PD, std::pair Note); @@ -1305,13 +1315,13 @@ public: bool IsForUsingDecl); bool IsOverload(FunctionDecl *New, FunctionDecl *Old, bool IsForUsingDecl); - bool TryImplicitConversion(InitializationSequence &Sequence, - const InitializedEntity &Entity, - Expr *From, - bool SuppressUserConversions, - bool AllowExplicit, - bool InOverloadResolution, - bool CStyle); + ImplicitConversionSequence + TryImplicitConversion(Expr *From, QualType ToType, + bool SuppressUserConversions, + bool AllowExplicit, + bool InOverloadResolution, + bool CStyle, + bool AllowObjCWritebackConversion); bool IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType); bool IsFloatingPointPromotion(QualType FromType, QualType ToType); @@ -1321,6 +1331,8 @@ public: QualType& ConvertedType, bool &IncompatibleObjC); bool isObjCPointerConversion(QualType FromType, QualType ToType, QualType& ConvertedType, bool &IncompatibleObjC); + bool isObjCWritebackConversion(QualType FromType, QualType ToType, + QualType &ConvertedType); bool IsBlockPointerConversion(QualType FromType, QualType ToType, QualType& ConvertedType); bool FunctionArgTypesAreEqual(const FunctionProtoType *OldType, @@ -1338,7 +1350,7 @@ public: CXXCastPath &BasePath, bool IgnoreBaseAccess); bool IsQualificationConversion(QualType FromType, QualType ToType, - bool CStyle); + bool CStyle, bool &ObjCLifetimeConversion); bool DiagnoseMultipleUserDefinedConversion(Expr *From, QualType ToType); @@ -1853,14 +1865,20 @@ public: void AtomicPropertySetterGetterRules(ObjCImplDecl* IMPDecl, ObjCContainerDecl* IDecl); + void DiagnoseOwningPropertyGetterSynthesis(const ObjCImplementationDecl *D); + void DiagnoseDuplicateIvars(ObjCInterfaceDecl *ID, ObjCInterfaceDecl *SID); + enum MethodMatchStrategy { + MMS_loose, + MMS_strict + }; + /// MatchTwoMethodDeclarations - Checks if two methods' type match and returns /// true, or false, accordingly. bool MatchTwoMethodDeclarations(const ObjCMethodDecl *Method, const ObjCMethodDecl *PrevMethod, - bool matchBasedOnSizeAndAlignment = false, - bool matchBasedOnStrictEqulity = false); + MethodMatchStrategy strategy = MMS_strict); /// MatchAllMethodDeclarations - Check methods declaraed in interface or /// or protocol against those declared in their implementations. @@ -2073,10 +2091,13 @@ public: Expr *SynchExpr, Stmt *SynchBody); + StmtResult ActOnObjCAutoreleasePoolStmt(SourceLocation AtLoc, Stmt *Body); + VarDecl *BuildExceptionDeclaration(Scope *S, TypeSourceInfo *TInfo, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id); + Decl *ActOnExceptionDeclarator(Scope *S, Declarator &D); StmtResult ActOnCXXCatchBlock(SourceLocation CatchLoc, @@ -2130,6 +2151,9 @@ public: void HandleDelayedDeprecationCheck(sema::DelayedDiagnostic &DD, Decl *Ctx); + bool makeUnavailableInSystemHeader(SourceLocation loc, + llvm::StringRef message); + //===--------------------------------------------------------------------===// // Expression Parsing Callbacks: SemaExpr.cpp. @@ -2146,6 +2170,8 @@ public: void PopExpressionEvaluationContext(); + void DiscardCleanupsInEvaluationContext(); + void MarkDeclarationReferenced(SourceLocation Loc, Decl *D); void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T); void MarkDeclarationsReferencedInExpr(Expr *E); @@ -4982,6 +5008,15 @@ public: IdentifierInfo *PropertyIvar, SourceLocation PropertyIvarLoc); + enum ObjCSpecialMethodKind { + OSMK_None, + OSMK_Alloc, + OSMK_New, + OSMK_Copy, + OSMK_RetainingInit, + OSMK_NonRetainingInit + }; + struct ObjCArgInfo { IdentifierInfo *Name; SourceLocation NameLoc; @@ -5020,6 +5055,8 @@ public: const ObjCObjectPointerType *OPT, bool IsInstance); + bool inferObjCARCLifetime(ValueDecl *decl); + ExprResult HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, Expr *BaseExpr, @@ -5098,6 +5135,22 @@ public: SourceLocation RBracLoc, MultiExprArg Args); + ExprResult BuildObjCBridgedCast(SourceLocation LParenLoc, + ObjCBridgeCastKind Kind, + SourceLocation BridgeKeywordLoc, + TypeSourceInfo *TSInfo, + Expr *SubExpr); + + ExprResult ActOnObjCBridgedCast(Scope *S, + SourceLocation LParenLoc, + ObjCBridgeCastKind Kind, + SourceLocation BridgeKeywordLoc, + ParsedType Type, + SourceLocation RParenLoc, + Expr *SubExpr); + + bool checkInitMethod(ObjCMethodDecl *method, QualType receiverTypeIfCall); + /// \brief Check whether the given new method is a valid override of the /// given overridden method, and set any properties that should be inherited. /// @@ -5105,7 +5158,7 @@ public: bool CheckObjCMethodOverride(ObjCMethodDecl *NewMethod, const ObjCMethodDecl *Overridden, bool IsImplementation); - + /// \brief Check whether the given method overrides any methods in its class, /// calling \c CheckObjCMethodOverride for each overridden method. bool CheckObjCMethodOverrides(ObjCMethodDecl *NewMethod, DeclContext *DC); @@ -5208,12 +5261,26 @@ public: /// from the inner expression. ExprValueKind CastCategory(Expr *E); + /// \brief The kind of conversion being performed. + enum CheckedConversionKind { + /// \brief An implicit conversion. + CCK_ImplicitConversion, + /// \brief A C-style cast. + CCK_CStyleCast, + /// \brief A functional-style cast. + CCK_FunctionalCast, + /// \brief A cast other than a C-style cast. + CCK_OtherCast + }; + /// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit /// cast. If there is already an implicit cast, merge into the existing one. /// If isLvalue, the result of the cast is an lvalue. ExprResult ImpCastExprToType(Expr *E, QualType Type, CastKind CK, ExprValueKind VK = VK_RValue, - const CXXCastPath *BasePath = 0); + const CXXCastPath *BasePath = 0, + CheckedConversionKind CCK + = CCK_ImplicitConversion); /// ScalarTypeToBooleanCastKind - Returns the cast kind corresponding /// to the conversion from scalar type ScalarTy to the Boolean type. @@ -5393,11 +5460,12 @@ public: ExprResult PerformImplicitConversion(Expr *From, QualType ToType, const ImplicitConversionSequence& ICS, AssignmentAction Action, - bool CStyle = false); + CheckedConversionKind CCK + = CCK_ImplicitConversion); ExprResult PerformImplicitConversion(Expr *From, QualType ToType, const StandardConversionSequence& SCS, AssignmentAction Action, - bool CStyle); + CheckedConversionKind CCK); /// the following "Check" methods will return a valid/converted QualType /// or a null QualType (indicating an error diagnostic was issued). @@ -5494,12 +5562,14 @@ public: ReferenceCompareResult CompareReferenceRelationship(SourceLocation Loc, QualType T1, QualType T2, bool &DerivedToBase, - bool &ObjCConversion); + bool &ObjCConversion, + bool &ObjCLifetimeConversion); /// CheckCastTypes - Check type constraints for casting between types under /// C semantics, or forward to CXXCheckCStyleCast in C++. - ExprResult CheckCastTypes(SourceRange TyRange, QualType CastTy, Expr *CastExpr, - CastKind &Kind, ExprValueKind &VK, CXXCastPath &BasePath, + ExprResult CheckCastTypes(SourceLocation CastStartLoc, SourceRange TyRange, + QualType CastTy, Expr *CastExpr, CastKind &Kind, + ExprValueKind &VK, CXXCastPath &BasePath, bool FunctionalStyle = false); ExprResult checkUnknownAnyCast(SourceRange TyRange, QualType castType, @@ -5526,6 +5596,20 @@ public: ExprResult CXXCheckCStyleCast(SourceRange R, QualType CastTy, ExprValueKind &VK, Expr *CastExpr, CastKind &Kind, CXXCastPath &BasePath, bool FunctionalStyle); + + /// \brief Checks for invalid conversions and casts between + /// retainable pointers and other pointer kinds. + void CheckObjCARCConversion(SourceRange castRange, QualType castType, + Expr *op, CheckedConversionKind CCK); + + /// checkRetainCycles - Check whether an Objective-C message send + /// might create an obvious retain cycle. + void checkRetainCycles(ObjCMessageExpr *msg); + void checkRetainCycles(Expr *receiver, Expr *argument); + + /// checkWeakUnsafeAssigns - Check whether +1 expr is being assigned + /// to weak/__unsafe_unretained. + void checkUnsafeAssigns(SourceLocation Loc, QualType LHS, Expr *RHS); /// CheckMessageArgumentTypes - Check types in an Obj-C message send. /// \param Method - May be null. @@ -5544,7 +5628,7 @@ public: QualType getMessageSendResultType(QualType ReceiverType, ObjCMethodDecl *Method, bool isClassMessage, bool isSuperMessage); - + /// \brief If the given expression involves a message send to a method /// with a related result type, emit a note describing what happened. void EmitRelatedResultTypeNote(const Expr *E); diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index c881b23ed1..12f701e460 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -922,6 +922,8 @@ namespace clang { EXPR_OBJC_MESSAGE_EXPR, /// \brief An ObjCIsa Expr record. EXPR_OBJC_ISA, + /// \breif An ObjCIndirectCopyRestoreExpr record. + EXPR_OBJC_INDIRECT_COPY_RESTORE, /// \brief An ObjCForCollectionStmt record. STMT_OBJC_FOR_COLLECTION, @@ -935,6 +937,8 @@ namespace clang { STMT_OBJC_AT_SYNCHRONIZED, /// \brief An ObjCAtThrowStmt record. STMT_OBJC_AT_THROW, + /// \brief An ObjCAutoreleasePoolStmt record. + STMT_OBJC_AUTORELEASE_POOL, // C++ @@ -1002,11 +1006,13 @@ namespace clang { EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK,// SubstNonTypeTemplateParmPackExpr // CUDA + EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr - EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr - // OpenCL - EXPR_ASTYPE // An AsTypeExpr record. + EXPR_ASTYPE, // AsTypeExpr + + // ARC + EXPR_OBJC_BRIDGED_CAST // ObjCBridgedCastExpr }; /// \brief The kinds of designators that can occur in a diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 4c66ed8d7e..fac2eacc4f 100644 --- a/lib/AST/ASTContext.cpp +++ b/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()) { @@ -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(Ty)) { - if (TypedefNameDecl *TD = TDT->getDecl()) - if (TD->getAttr()) - 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/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 08ac2a5be4..c10e6c40d3 100644 --- a/lib/AST/DeclCXX.cpp +++ b/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/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index e2c4f38ff9..99eb0d386b 100644 --- a/lib/AST/DeclObjC.cpp +++ b/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(); + + // 'self' is always __strong, although as a special case we don't + // actually retain it except in init methods. + Qualifiers qs; + qs.setObjCLifetime(Qualifiers::OCL_Strong); + selfTy = Context.getQualifiedType(selfTy, qs); + + // In addition, 'self' is const unless this is an init method. + if (getMethodFamily() != OMF_init) + selfTy = selfTy.withConst(); + } + + ImplicitParamDecl *self + = ImplicitParamDecl::Create(Context, this, SourceLocation(), + &Context.Idents.get("self"), selfTy); + setSelfDecl(self); + + if (selfIsConsumed) + self->addAttr(new (Context) NSConsumedAttr(SourceLocation(), Context)); setCmdDecl(ImplicitParamDecl::Create(Context, this, SourceLocation(), &Context.Idents.get("_cmd"), diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index 421770ea70..19554a3baa 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/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/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 987213907e..97f4ea6cba 100644 --- a/lib/AST/Expr.cpp +++ b/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(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()) { 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(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(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(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/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp index d177cb5cbc..1a1fa91a40 100644 --- a/lib/AST/ExprClassification.cpp +++ b/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(E)->getTypeAsWritten()); diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 06c5645afb..432ffee08d 100644 --- a/lib/AST/ExprConstant.cpp +++ b/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(E)->getSubExpr(); if (SubExpr->getType()->isIntegralOrEnumerationType()) return CheckICE(SubExpr, Ctx); diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index e81ec7e54b..a77fe5f48a 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -21,6 +21,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/ABI.h" #include "clang/Basic/SourceManager.h" @@ -1464,7 +1465,35 @@ 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: + // + // ::= U "__strong" + // ::= U "__weak" + // ::= U "__autoreleasing" + // ::= 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(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(E)->getBridgeKindName(); + Out << "v1U" << Kind.size() << Kind; + } + // Fall through to mangle the cast itself. + case Expr::CStyleCastExprClass: case Expr::CXXStaticCastExprClass: case Expr::CXXDynamicCastExprClass: diff --git a/lib/AST/ParentMap.cpp b/lib/AST/ParentMap.cpp index eca351aec8..b7b2005e9f 100644 --- a/lib/AST/ParentMap.cpp +++ b/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(S)) { + Paren = S; + S = getParent(S); + }; + return Paren; +} + bool ParentMap::isConsumedExpr(Expr* E) const { Stmt *P = getParent(E); Stmt *DirectChild = E; diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 87588e4518..d6a67b13ef 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/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(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/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index b117cd9a52..c70c87a724 100644 --- a/lib/AST/StmtProfile.cpp +++ b/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/lib/AST/Type.cpp b/lib/AST/Type.cpp index 938a686c2b..080bca2198 100644 --- a/lib/AST/Type.cpp +++ b/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(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(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(cast(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()) { + if (const CXXRecordDecl *ClassDecl = + dyn_cast(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()) { + if (const CXXRecordDecl *ClassDecl = + dyn_cast(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()) { - if (const CXXRecordDecl *ClassDecl = - dyn_cast(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()) { - if (const CXXRecordDecl *ClassDecl = - dyn_cast(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(this+1); @@ -1423,6 +1522,12 @@ FunctionProtoType::FunctionProtoType(QualType result, const QualType *args, Expr **noexSlot = reinterpret_cast(argSlot + numArgs); *noexSlot = epi.NoexceptExpr; } + + if (epi.ConsumedArguments) { + bool *consumedArgs = const_cast(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(canon)) + canon = array->getElementType().getTypePtr(); + + if (const ObjCObjectPointerType *opt + = dyn_cast(canon)) { + // Class and Class don't require retension. + if (opt->getObjectType()->isObjCClass()) + return true; + } + + return false; +} + +bool Type::isObjCNSObjectType() const { + if (const TypedefType *typedefType = dyn_cast(this)) + return typedefType->getDecl()->hasAttr(); + return false; +} +bool Type::isObjCRetainableType() const { + return isObjCObjectPointerType() || + isBlockPointerType() || + isObjCNSObjectType(); +} +bool Type::isObjCIndirectLifetimeType() const { + if (isObjCLifetimeType()) + return true; + if (const PointerType *OPT = getAs()) + return OPT->getPointeeType()->isObjCIndirectLifetimeType(); + if (const ReferenceType *Ref = getAs()) + return Ref->getPointeeType()->isObjCIndirectLifetimeType(); + if (const MemberPointerType *MemPtr = getAs()) + 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(); + 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/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index 4519606f6e..ccb456018d 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -24,6 +24,23 @@ using namespace clang; namespace { + /// \brief RAII object that enables printing of the ARC __strong lifetime + /// qualifier. + class IncludeStrongLifetimeRAII { + PrintingPolicy &Policy; + bool Old; + + public: + explicit IncludeStrongLifetimeRAII(PrintingPolicy &Policy) + : Policy(Policy), Old(Policy.SuppressStrongLifetime) { + Policy.SuppressStrongLifetime = false; + } + + ~IncludeStrongLifetimeRAII() { + Policy.SuppressStrongLifetime = Old; + } + }; + class TypePrinter { PrintingPolicy Policy; @@ -78,7 +95,7 @@ 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(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(T->getPointeeType())) S = '(' + S + ')'; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getPointeeType(), S); } @@ -209,6 +244,7 @@ void TypePrinter::printLValueReference(const LValueReferenceType *T, if (isa(T->getPointeeTypeAsWritten())) S = '(' + S + ')'; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getPointeeTypeAsWritten(), S); } @@ -221,6 +257,7 @@ void TypePrinter::printRValueReference(const RValueReferenceType *T, if (isa(T->getPointeeTypeAsWritten())) S = '(' + S + ')'; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getPointeeTypeAsWritten(), S); } @@ -236,6 +273,7 @@ void TypePrinter::printMemberPointer(const MemberPointerType *T, if (isa(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 += ""; } else if (ClassTemplateSpecializationDecl *Spec = dyn_cast(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/lib/Analysis/ReachableCode.cpp b/lib/Analysis/ReachableCode.cpp index 9ac456f53a..c5b17fc77b 100644 --- a/lib/Analysis/ReachableCode.cpp +++ b/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(S)->getHandler(0)->getCatchLoc(); } + case Expr::ObjCBridgedCastExprClass: { + const ObjCBridgedCastExpr *CSC = cast(S); + R1 = CSC->getSubExpr()->getSourceRange(); + return CSC->getLParenLoc(); + } default: ; } R1 = S->getSourceRange(); diff --git a/lib/Basic/DiagnosticIDs.cpp b/lib/Basic/DiagnosticIDs.cpp index 25dbcf0a6f..b5b48cb213 100644 --- a/lib/Basic/DiagnosticIDs.cpp +++ b/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/lib/Basic/IdentifierTable.cpp b/lib/Basic/IdentifierTable.cpp index 4711faa1a5..109761c9dc 100644 --- a/lib/Basic/IdentifierTable.cpp +++ b/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 @@ -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/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index dd167dca47..541a1ed595 100644 --- a/lib/Basic/Targets.cpp +++ b/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/lib/CodeGen/CGBlocks.cpp b/lib/CodeGen/CGBlocks.cpp index e5da703a61..ac47034325 100644 --- a/lib/CodeGen/CGBlocks.cpp +++ b/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(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(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/lib/CodeGen/CGBlocks.h b/lib/CodeGen/CGBlocks.h index 9bd18e5fde..35d61cfdd4 100644 --- a/lib/CodeGen/CGBlocks.h +++ b/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/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 05c11ceac6..712ec62bd4 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/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()) + 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(result); + + llvm::SmallVector insnsToKill; + + // Look for: + // %generator = bitcast %type1* %generator2 to %type2* + while (llvm::BitCastInst *bitcast = dyn_cast(generator)) { + // We would have emitted this as a constant if the operand weren't + // an Instruction. + generator = cast(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(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(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(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::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(addr); +} + +static bool isProvablyNonNull(llvm::Value *addr) { + return isa(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(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(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()->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(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(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/lib/CodeGen/CGCall.h b/lib/CodeGen/CGCall.h index 160a62eab3..343b944bf6 100644 --- a/lib/CodeGen/CGCall.h +++ b/lib/CodeGen/CGCall.h @@ -58,9 +58,44 @@ namespace CodeGen { class CallArgList : public llvm::SmallVector { 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::const_iterator writeback_iterator; + writeback_iterator writeback_begin() const { return Writebacks.begin(); } + writeback_iterator writeback_end() const { return Writebacks.end(); } + + private: + llvm::SmallVector 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/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index 5725d80b7d..066f0d5c7d 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/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(); - if (!Record || - cast(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(); - if (!RT) + + switch (FieldType.isDestructedType()) { + case QualType::DK_none: continue; - - CXXRecordDecl *FieldClassDecl = cast(RT->getDecl()); - if (FieldClassDecl->hasTrivialDestructor()) - continue; + + case QualType::DK_cxx_destructor: + if (Array) + EHStack.pushCleanup(NormalAndEHCleanup, Field); + else + EHStack.pushCleanup(NormalAndEHCleanup, Field); + break; + + case QualType::DK_objc_strong_lifetime: + PushARCFieldReleaseCleanup(getARCCleanupKind(), Field); + break; - if (Array) - EHStack.pushCleanup(NormalAndEHCleanup, Field); - else - EHStack.pushCleanup(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/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 98d30db647..88cc5be4de 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/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/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index 33c4ada68f..14e999f589 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/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()); + 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(s)) { + // Skip the most common kinds of expressions that make + // hierarchy-walking expensive. + s = e = e->IgnoreParenCasts(); + + if (const DeclRefExpr *ref = dyn_cast(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(decl)) return false; + const VarDecl *var = cast(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(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(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(D))); + } + + const llvm::PointerType *ty = cast(tempAddr->getType()); + ty = cast(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(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(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(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(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()) { 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(); + + // '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(D)) { + const ObjCMethodDecl *method = cast(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(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/lib/CodeGen/CGDeclCXX.cpp b/lib/CodeGen/CGDeclCXX.cpp index 178badd44d..08e8e0c48e 100644 --- a/lib/CodeGen/CGDeclCXX.cpp +++ b/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/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp index e8ad6da2f9..ce57d02bcd 100644 --- a/lib/CodeGen/CGException.cpp +++ b/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/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 525ad1b8f1..197bc67881 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/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(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()) { @@ -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(InitializedDecl)) { + if (const ReferenceType *RefType + = InitVD->getType()->getAs()) { + 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(InitializedDecl)) { - if (VD->hasGlobalStorage()) { + const VarDecl *VD = dyn_cast_or_null(InitializedDecl); + if (VD && VD->hasGlobalStorage()) { + if (ReferenceTemporaryDtor) { llvm::Constant *DtorFn = CGM.GetAddrOfCXXDestructor(ReferenceTemporaryDtor, Dtor_Complete); EmitCXXGlobalDtorRegistration(DtorFn, cast(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()); + 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(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(E)) { + setObjCGCLValueClass(Ctx, Exp->getSubExpr(), LV); + return; + } + if (const ArraySubscriptExpr *Exp = dyn_cast(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(TargetDecl)) return EmitCXXOperatorMemberCallExpr(CE, MD, ReturnValue); - if (isa(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(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(); + 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/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp index d8da642a6b..6d34499f38 100644 --- a/lib/CodeGen/CGExprAgg.cpp +++ b/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()) { + // 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()) { 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()) { if (RecordTy->getDecl()->hasObjectMember()) { diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index 81fee677f6..434ca1b69f 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/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/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp index da37bd5b0a..75e566110c 100644 --- a/lib/CodeGen/CGExprConstant.cpp +++ b/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/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index dff7bf45e0..6e42da8e01 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/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/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index cdb15bfd83..cdc2fffd94 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/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 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(addr->getType())->getElementType(); + return llvm::ConstantPointerNull::get(cast(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()); + 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(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(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(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(CGF.CurCodeDecl); + const ObjCImplementationDecl *impl + = cast(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(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(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 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(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(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(value)) return value; + + if (!fn) { + std::vector 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 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(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(addr->getType())->getElementType() + == value->getType()); + + if (!fn) { + std::vector 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 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(value)) return; + + llvm::Constant *&fn = CGM.getARCEntrypoints().objc_release; + if (!fn) { + std::vector 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 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(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(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(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 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 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(arrayType)) { + vlaSizeInBytes = CGF.GetVLASize(cast(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(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 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( + cast(addr->getType())->getElementType()); + while (true) { + assert(isa(arrayType)); + assert(cast(arrayType)->getSize().getZExtValue() + == llvmArrayType->getNumElements()); + + gepIndices.push_back(zero); + countFromCLAs *= llvmArrayType->getNumElements(); + + llvmArrayType = + dyn_cast(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(NormalCleanup, Ptr); + else + EHStack.pushCleanup(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(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(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(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(cleanupKind, field); +} + +static void pushReleaseForIvar(CodeGenFunction &CGF, ObjCIvarDecl *ivar, + llvm::Value *self) { + CGF.EHStack.pushCleanup(CGF.getARCCleanupKind(), + ivar, self); +} + +static void pushWeakReleaseForIvar(CodeGenFunction &CGF, ObjCIvarDecl *ivar, + llvm::Value *self) { + CGF.EHStack.pushCleanup(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(e) && + cast(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(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(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(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(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(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(e) || + (isa(e) && + !cast(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 +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, value); +} + +std::pair +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, value); +} + +void CodeGenFunction::EmitObjCAutoreleasePoolStmt( + const ObjCAutoreleasePoolStmt &ARPS) { + const Stmt *subStmt = ARPS.getSubStmt(); + const CompoundStmt &S = cast(*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(NormalCleanup, token); + } else { + llvm::Value *token = EmitObjCMRRAutoreleasePoolPush(); + EHStack.pushCleanup(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/lib/CodeGen/CGObjCGNU.cpp b/lib/CodeGen/CGObjCGNU.cpp index f0993c5dad..e85c8980fe 100644 --- a/lib/CodeGen/CGObjCGNU.cpp +++ b/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/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 8c3e9a36e3..988b16e945 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/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(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(Field)); + FieldOffset = + ComputeIvarBaseOffset(CGM, OI, cast(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 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 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 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/lib/CodeGen/CGObjCRuntime.h b/lib/CodeGen/CGObjCRuntime.h index 866d5d83fe..7accc70c96 100644 --- a/lib/CodeGen/CGObjCRuntime.h +++ b/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/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp index a982621be7..187d5a6c3c 100644 --- a/lib/CodeGen/CGStmt.cpp +++ b/lib/CodeGen/CGStmt.cpp @@ -151,6 +151,9 @@ void CodeGenFunction::EmitStmt(const Stmt *S) { case Stmt::ObjCForCollectionStmtClass: EmitObjCForCollectionStmt(cast(*S)); break; + case Stmt::ObjCAutoreleasePoolStmtClass: + EmitObjCAutoreleasePoolStmt(cast(*S)); + break; case Stmt::CXXTryStmtClass: EmitCXXTryStmt(cast(*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/lib/CodeGen/CGValue.h b/lib/CodeGen/CGValue.h index 7f77d55203..1ad6628ec6 100644 --- a/lib/CodeGen/CGValue.h +++ b/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/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index 150cb69b4d..6ab4b76e75 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/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(D) && cast(D)->isInstance()) diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index bb8fd8e243..f35022810f 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/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 + EmitARCStoreAutoreleasing(const BinaryOperator *e); + std::pair + 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/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 7a1a968259..78c57b4e3f 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/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/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 09741dfada..7321ac45f4 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/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/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 12ef9bd030..7e71df1268 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/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/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp index 043ead7e62..9a6cc208e5 100644 --- a/lib/CodeGen/TargetInfo.cpp +++ b/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/lib/CodeGen/TargetInfo.h b/lib/CodeGen/TargetInfo.h index 4f59eb619e..dbb09b1614 100644 --- a/lib/CodeGen/TargetInfo.h +++ b/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/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp index d919915410..8079e1983f 100644 --- a/lib/Driver/ToolChain.cpp +++ b/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/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index ca613e3d6c..35e7d80c29 100644 --- a/lib/Driver/ToolChains.cpp +++ b/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/lib/Driver/ToolChains.h b/lib/Driver/ToolChains.h index ace9b84791..7325874473 100644 --- a/lib/Driver/ToolChains.h +++ b/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/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 0e85a9fd4f..1947827438 100644 --- a/lib/Driver/Tools.cpp +++ b/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/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index c929b4926d..1324926f94 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/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(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/lib/Frontend/InitPreprocessor.cpp b/lib/Frontend/InitPreprocessor.cpp index 147a8df037..f1e1d47713 100644 --- a/lib/Frontend/InitPreprocessor.cpp +++ b/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 \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 \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 \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 \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 struct __is_scalar;\n" + << "\n"; + + Out << "template\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\n" + << "struct __is_scalar<__attribute__((objc_lifetime(weak))) _Tp> {\n" + << " enum { __value = 0 };\n" + << " typedef __false_type __type;\n" + << "};\n" + << "\n"; + } + + Out << "template\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 \"\" 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/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index 01cd75fa84..9e8533a448 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/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/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index ad3fcfe0d3..2d3fcf8340 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/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/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index d349220e3d..01bd0ed336 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/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/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index a8c18c01a2..4f28321827 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/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/lib/Rewrite/FixItRewriter.cpp b/lib/Rewrite/FixItRewriter.cpp index 8dcc5dcba7..e50793e277 100644 --- a/lib/Rewrite/FixItRewriter.cpp +++ b/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/lib/Rewrite/Rewriter.cpp b/lib/Rewrite/Rewriter.cpp index 51fe379fee..92f516074e 100644 --- a/lib/Rewrite/Rewriter.cpp +++ b/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 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/lib/Sema/AttributeList.cpp b/lib/Sema/AttributeList.cpp index 619a5b961b..fda4e0ccd8 100644 --- a/lib/Sema/AttributeList.cpp +++ b/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/lib/Sema/DelayedDiagnostic.cpp b/lib/Sema/DelayedDiagnostic.cpp index af548fe134..c6744ed80d 100644 --- a/lib/Sema/DelayedDiagnostic.cpp +++ b/lib/Sema/DelayedDiagnostic.cpp @@ -47,5 +47,8 @@ void DelayedDiagnostic::Destroy() { case Deprecation: delete [] DeprecationData.Message; break; + + case ForbiddenType: + break; } } diff --git a/lib/Sema/JumpDiagnostics.cpp b/lib/Sema/JumpDiagnostics.cpp index 679f4fefa2..007d755a87 100644 --- a/lib/Sema/JumpDiagnostics.cpp +++ b/lib/Sema/JumpDiagnostics.cpp @@ -111,90 +111,110 @@ unsigned JumpScopeChecker::GetDeepestCommonScope(unsigned A, unsigned B) { return A; } +typedef std::pair 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 - GetDiagForGotoScopeDecl(const Decl *D, bool isCPlusPlus) { +static ScopePair GetDiagForGotoScopeDecl(ASTContext &Context, const Decl *D) { if (const VarDecl *VD = dyn_cast(D)) { unsigned InDiag = 0, OutDiag = 0; if (VD->getType()->isVariablyModifiedType()) InDiag = diag::note_protected_by_vla; - if (VD->hasAttr()) { - InDiag = diag::note_protected_by___block; - OutDiag = diag::note_exits___block; - } else if (VD->hasAttr()) { - 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(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()) + return ScopePair(diag::note_protected_by___block, + diag::note_exits___block); + + if (VD->hasAttr()) + 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(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(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(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 Diags - = GetDiagForGotoScopeDecl(D, isCPlusPlus); + std::pair 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(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/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 8297b3155a..7d22031ecb 100644 --- a/lib/Sema/Sema.cpp +++ b/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(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()) 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/lib/Sema/SemaCXXCast.cpp b/lib/Sema/SemaCXXCast.cpp index e46ad5bcfe..0b3083aaa6 100644 --- a/lib/Sema/SemaCXXCast.cpp +++ b/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\(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/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 389dd91f32..d45ebf9033 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/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(); + if (!pointerType) { Diag(DRE->getLocStart(), diag::err_atomic_builtin_must_be_pointer) << FirstArg->getType() << FirstArg->getSourceRange(); return ExprError(); } - QualType ValType = - FirstArg->getType()->getAs()->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()) { 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 &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(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(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(pre->getBase()); + continue; + } + + default: + return false; + } + } + + if (ObjCIvarRefExpr *ref = dyn_cast(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(e)) { + VarDecl *var = dyn_cast(ref->getDecl()); + if (!var) return false; + return considerVariable(var, ref, owner); + } + + if (BlockDeclRefExpr *ref = dyn_cast(e)) { + owner.Variable = ref->getDecl(); + owner.setLocsFrom(ref); + return true; + } + + if (MemberExpr *member = dyn_cast(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(ASTContext &Context, VarDecl *variable) + : EvaluatedExprVisitor(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(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(RHS)) + if (cast->getCastKind() == CK_ObjCConsumeObject) + Diag(Loc, diag::warn_arc_retained_assign) + << (LT == Qualifiers::OCL_ExplicitNone) + << RHS->getSourceRange(); +} + diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index e328eeb0aa..69b38593fb 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/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 ) + 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 ) + 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 ) + 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(T)) @@ -1878,9 +1907,9 @@ static void AddResultTypeChunk(ASTContext &Context, T = Context.getTypeDeclType(cast(Enumerator->getDeclContext())); else if (isa(ND)) { /* Do nothing: ignore unresolved using declarations*/ - } else if (ValueDecl *Value = dyn_cast(ND)) + } else if (ValueDecl *Value = dyn_cast(ND)) { T = Value->getType(); - else if (ObjCPropertyDecl *Property = dyn_cast(ND)) + } else if (ObjCPropertyDecl *Property = dyn_cast(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(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(*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(*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(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(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/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index c7545cf532..4476211bc0 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/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(decl)) { + if (var->hasAttr()) + kind = 0; // __block + else if (!var->hasLocalStorage()) + kind = 1; // global + } else if (isa(decl)) { + kind = 3; // ivar + } else if (isa(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(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(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(dcl)) { + ExprNeedsCleanups = false; + } else if (!isa(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()) + 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 RecFields; RecordDecl *Record = dyn_cast(EnclosingDecl); + bool ARCErrReported = false; for (unsigned i = 0; i != NumFields; ++i) { FieldDecl *FD = cast(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()) { + 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()->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()->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/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 430b4f99fb..4769b09264 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/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(d) || isa(d) || isa(d); + // In some sense, TypedefDecl really *ought* to be a DeclaratorDecl. + return isa(d) || isa(d) || isa(d) || + isa(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(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(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(d) && !isa(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(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(decl) && !isa(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/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index ce99efbd0b..09597d2ef1 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/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()) - 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(BaseType->getAs()->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/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index bfa2ef45d0..563de6fd15 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/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()->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(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(method->getDeclContext())) { + if (receiverTypeIfCall.isNull()) + return false; + + receiverClass = receiverTypeIfCall->castAs() + ->getInterfaceDecl(); + + // This can be null for calls to e.g. id. + 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::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(DC)) { // Look through protocols. for (ObjCList::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(DC)) { // Look through protocols. for (ObjCList::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(DC)) return ::CheckObjCMethodOverrides(*this, NewMethod, Class); - + if (ObjCCategoryDecl *Category = dyn_cast(DC)) return ::CheckObjCMethodOverrides(*this, NewMethod, Category); - + if (ObjCProtocolDecl *Protocol = dyn_cast(DC)) return ::CheckObjCMethodOverrides(*this, NewMethod, Protocol); - + if (ObjCImplementationDecl *Impl = dyn_cast(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()) + return false; + break; + + case OMF_alloc: + case OMF_copy: + case OMF_mutableCopy: + case OMF_new: + if (method->hasAttr() || + method->hasAttr() || + method->hasAttr()) + 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(left)) return isa(right); + if (isa(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 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() + != right->hasAttr() || + left->hasAttr() + != right->hasAttr())) + 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() != rparm->hasAttr()) + 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/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index a2ad16145a..f0ecb5800c 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -87,7 +87,7 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, if (FunctionDecl *FD = dyn_cast(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(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(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()) { + if (const PointerType *ExprPtr = castExprType->getAs()) { + 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(E->IgnoreParenCasts()); + if (declRef && isa(declRef->getDecl())) { + VarDecl *var = cast(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()); // 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(); + } } - 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(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(s); + if (!cleanups) return 0; + + ImplicitCastExpr *cast = dyn_cast(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(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(Body)); BSI->TheDecl->setBody(cast(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/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 2f5a890e51..c796f12f08 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/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()) @@ -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()) return cast(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()) return cast(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()) return cast(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()) { CXXRecordDecl* RD = cast(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()) { CXXRecordDecl *RD = cast(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()) { CXXRecordDecl *RD = cast(RT->getDecl()); @@ -3828,17 +3908,84 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) { if (!E) return ExprError(); - if (!Context.getLangOptions().CPlusPlus) + assert(!isa(E) && "Double-bound temporary?"); + + // If the result is a glvalue, we shouldn't bind it. + if (!E->isRValue()) return Owned(E); - assert(!isa(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(E)) { + Expr *Callee = Call->getCallee()->IgnoreParens(); + QualType T = Callee->getType(); + + if (T == Context.BoundMemberTy) { + // Handle pointer-to-members. + if (BinaryOperator *BinOp = dyn_cast(Callee)) + T = BinOp->getRHS()->getType(); + else if (MemberExpr *Mem = dyn_cast(Callee)) + T = Mem->getMemberDecl()->getType(); + } + + if (const PointerType *Ptr = T->getAs()) + T = Ptr->getPointeeType(); + else if (const BlockPointerType *Ptr = T->getAs()) + T = Ptr->getPointeeType(); + else if (const MemberPointerType *MemPtr = T->getAs()) + T = MemPtr->getPointeeType(); + + const FunctionType *FTy = T->getAs(); + 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(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(E)) { + D = Send->getMethodDecl(); + } else { + CastExpr *CE = cast(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(); - if (!RT) + ReturnsRetained = (D && D->hasAttr()); + } + + 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(); + 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/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index cb5c1e0d0c..1d1f6aeb34 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/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(DC)) DC = DC->getParent(); if (DC && !isa(DC)) return false; - if (ImplicitCastExpr *ICE = dyn_cast(RExpr)) - if (ICE->getCastKind() == CK_LValueToRValue) - RExpr = ICE->getSubExpr(); - if (DeclRefExpr *DRE = dyn_cast(RExpr)) + receiver = receiver->IgnoreParenLValueCasts(); + if (DeclRefExpr *DRE = dyn_cast(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(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()) + type = ref->getPointeeType(); + + // Drill through pointers and arrays recursively. + while (true) { + if (const PointerType *ptr = type->getAs()) { + 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 { + 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(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()) { + if (const PointerType *CastExprPtr = castExprType->getAs()) { + 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/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index a33f5d0b2f..77932151c1 100644 --- a/lib/Sema/SemaInit.cpp +++ b/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(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(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(e)) { + if (op->getOpcode() == UO_AddrOf) + return isInvalidICRSource(C, op->getSubExpr()); + + // Skip certain casts. + } else if (CastExpr *cast = dyn_cast(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(e)) { + if (VarDecl *var = dyn_cast(declRef->getDecl())) + return (var->hasLocalStorage() ? IIK_okay : IIK_nonlocal); + + // If we have a conditional operator, check both sides. + } else if (ConditionalOperator *cond = dyn_cast(e)) { + if (InvalidICRKind iik = isInvalidICRSource(C, cond->getLHS())) + return iik; + + return isInvalidICRSource(C, cond->getRHS()); + + // These are never scalar. + } else if (isa(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(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(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/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index 6c4469cef9..2dbb7401dd 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/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(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(PropertyIvarType) - && isa(IvarType)) + && isa(IvarType)) compat = Context.canAssignObjCInterfaces( PropertyIvarType->getAs(), @@ -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(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()) { 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/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 4bba6f8877..2995e2e6e0 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/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(), 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() && 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()) + 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()) + 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(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/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index d7c0a543ee..18e5f7e3a4 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/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(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(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()) { 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(D)->getType(); + VarDecl *D = cast(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(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(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/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index d458050585..b8caad8af5 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -3503,9 +3503,11 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S, return true; } + bool ObjCLifetimeConversion; if (ParamType->isPointerType() && !ParamType->getAs()->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/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 9011cdf753..359c93130a 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/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/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index ef3c2624b2..bfffb9e41b 100644 --- a/lib/Sema/SemaType.cpp +++ b/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 ArgTys; ArgTys.reserve(FTI.NumArgs); + llvm::SmallVector ConsumedArguments; + ConsumedArguments.reserve(FTI.NumArgs); + bool HasAnyConsumedArguments = false; + for (unsigned i = 0, e = FTI.NumArgs; i != e; ++i) { ParmVarDecl *Param = cast(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(); + ConsumedArguments.push_back(Consumed); + HasAnyConsumedArguments |= Consumed; + } + ArgTys.push_back(ArgTy); } + if (HasAnyConsumedArguments) + EPI.ConsumedArguments = ConsumedArguments.data(); + llvm::SmallVector 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/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index ff2e46a902..ba4ee3426a 100644 --- a/lib/Sema/TreeTransform.h +++ b/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::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(Result); @@ -3333,7 +3350,11 @@ QualType TreeTransform::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(Result); NewT.setSigilLoc(TL.getSigilLoc()); return Result; @@ -3387,6 +3408,11 @@ TreeTransform::TransformReferenceType(TypeLocBuilder &TLB, return QualType(); } + // Objective-C ARC can add lifetime qualifiers to the type that we're + // referring to. + TLB.TypeWasModifiedSafely( + Result->getAs()->getPointeeTypeAsWritten()); + // r-value references can be rebuilt as l-value references. ReferenceTypeLoc NewTL; if (isa(Result)) @@ -5433,6 +5459,25 @@ TreeTransform::TransformObjCAtSynchronizedStmt( Object.get(), Body.get()); } +template +StmtResult +TreeTransform::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 StmtResult TreeTransform::TransformObjCForCollectionStmt( @@ -7608,6 +7653,43 @@ TreeTransform::TransformObjCEncodeExpr(ObjCEncodeExpr *E) { E->getRParenLoc()); } +template +ExprResult TreeTransform:: +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 +ExprResult TreeTransform:: +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 ExprResult TreeTransform::TransformObjCMessageExpr(ObjCMessageExpr *E) { diff --git a/lib/Sema/TypeLocBuilder.h b/lib/Sema/TypeLocBuilder.h index 3570737d11..f0944f1205 100644 --- a/lib/Sema/TypeLocBuilder.h +++ b/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 TyLocType push(QualType T) { diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 8fb20d22b7..1ee54b8f02 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/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(Record[4])); + static_cast(Record[4]), + /*produces*/ Record[5]); - unsigned Idx = 5; + unsigned Idx = 6; unsigned NumParams = Record[Idx++]; llvm::SmallVector ParamTypes; for (unsigned I = 0; I != NumParams; ++I) diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index f3f67a76c4..0af3546223 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/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(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/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index ba9032e0d3..80e790c796 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/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/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index 00e2404946..c6d182d425 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/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/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp index bc1d823dde..ec2a88a556 100644 --- a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -254,13 +254,13 @@ public: return; if (Expr* E = V->getInit()) { + while (ExprWithCleanups *exprClean = dyn_cast(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(E)) return; - - if (isa(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/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 07de8706df..73ce359edc 100644 --- a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/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(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, 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(vR)) + return true; + if (const StackSpaceRegion *SSR = dyn_cast(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/lib/StaticAnalyzer/Core/CFRefCount.cpp b/lib/StaticAnalyzer/Core/CFRefCount.cpp index 0512e2f08d..33118fa901 100644 --- a/lib/StaticAnalyzer/Core/CFRefCount.cpp +++ b/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(); new (Summ) RetainSummary(*OldSumm); @@ -1654,7 +1671,6 @@ public: const char* nl, const char* sep); }; -private: typedef llvm::DenseMap 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, check::RegionChanges > { + : public Checker< check::PostStmt, + check::PostStmt, + 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(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(Sym); + if (!T) + return; + + // This is gross. Once the checker and CFRefCount are unified, + // this will go away. + CFRefCount &cf = static_cast(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/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index aed39eb0ce..21efbac699 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/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(S), Pred, Dst); break; + case Stmt::ImplicitValueInitExprClass: { + const GRState *state = GetState(Pred); + QualType ty = cast(S)->getType(); + SVal val = svalBuilder.makeZeroVal(ty); + MakeNode(Dst, S, Pred, state->BindExpr(S, val)); + break; + } + + case Stmt::ExprWithCleanupsClass: { + Visit(cast(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(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: diff --git a/test/Analysis/objc-arc.m b/test/Analysis/objc-arc.m new file mode 100644 index 0000000000..6b22fd099b --- /dev/null +++ b/test/Analysis/objc-arc.m @@ -0,0 +1,149 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core -analyzer-checker=deadcode -analyzer-store=region -verify -fblocks -analyzer-opt-analyze-nested-blocks -fobjc-nonfragile-abi -fobjc-arc %s + +typedef signed char BOOL; +typedef struct _NSZone NSZone; +@class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator; + +@protocol NSObject +- (BOOL)isEqual:(id)object; +@end +@protocol NSCopying +- (id)copyWithZone:(NSZone *)zone; +@end +@protocol NSCoding +- (void)encodeWithCoder:(NSCoder *)aCoder; +@end +@interface NSObject {} ++ (id)alloc; +@end +typedef const struct __CFAllocator * CFAllocatorRef; +extern const CFAllocatorRef kCFAllocatorDefault; +typedef double CFTimeInterval; +typedef CFTimeInterval CFAbsoluteTime; +extern CFAbsoluteTime CFAbsoluteTimeGetCurrent(void); +typedef const struct __CFDate * CFDateRef; +extern CFDateRef CFDateCreate(CFAllocatorRef allocator, CFAbsoluteTime at); + +typedef const void* objc_objectptr_t; +__attribute__((ns_returns_retained)) id objc_retainedObject(objc_objectptr_t __attribute__((cf_consumed)) pointer); +__attribute__((ns_returns_not_retained)) id objc_unretainedObject(objc_objectptr_t pointer); + +// Test the analyzer is working at all. +void test_working() { + int *p = 0; + *p = 0xDEADBEEF; // expected-warning {{null}} +} + +// Test that in ARC mode that blocks are correctly automatically copied +// and not flagged as warnings by the analyzer. +typedef void (^Block)(void); +void testblock_bar(int x); + +Block testblock_foo(int x) { + Block b = ^{ testblock_bar(x); }; + return b; // no-warning +} + +Block testblock_baz(int x) { + return ^{ testblock_bar(x); }; // no-warning +} + +Block global_block; + +void testblock_qux(int x) { + global_block = ^{ testblock_bar(x); }; // no-warning +} + +// Test that Objective-C pointers are null initialized. +void test_nil_initialized() { + id x; + if (x == 0) + return; + int *p = 0; + *p = 0xDEADBEEF; // no-warning +} + +// Test that we don't flag leaks of Objective-C objects. +void test_alloc() { + [NSObject alloc]; // no-warning +} + +// Test that CF allocations are still caught as leaks. +void test_cf_leak() { + CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); + CFDateRef date = CFDateCreate(0, t); // expected-warning {{Potential leak}} + (void) date; +} + +// Test that 'init' methods do not try to claim ownerhip of an *unowned* allocated object +// in ARC mode. +@interface RDar9424890_A : NSObject +- (id)initWithCleaner:(int)pop mop:(NSString *)mop ; +- (RDar9424890_A *)rdar9424890:(NSString *)identifier; +@end +@interface RDar9424890_B : NSObject +@end +@implementation RDar9424890_B +- (RDar9424890_A *)obj:(RDar9424890_A *)obj { + static NSString *WhizFiz = @"WhizFiz"; + RDar9424890_A *cell = [obj rdar9424890:WhizFiz]; + if (cell == ((void*)0)) { + cell = [[RDar9424890_A alloc] initWithCleaner:0 mop:WhizFiz]; // no-warning + } + return cell; +} +@end + +// Test that dead store checking works in the prescence of "cleanups" in the AST. +void rdar9424882() { + id x = [NSObject alloc]; // expected-warning {{Value stored to 'x' during its initialization is never read}} +} + +// Test +typedef const void *CFTypeRef; +typedef const struct __CFString *CFStringRef; + +@interface NSString +- (id) self; +@end + +CFTypeRef CFCreateSomething(); +CFStringRef CFCreateString(); +CFTypeRef CFGetSomething(); +CFStringRef CFGetString(); + +id CreateSomething(); +NSString *CreateNSString(); + +void from_cf() { + id obj1 = (__bridge_transfer id)CFCreateSomething(); // expected-warning{{never read}} + id obj2 = (__bridge_transfer NSString*)CFCreateString(); + [obj2 self]; // Add a use, to show we can use the object after it has been transfered. + id obj3 = (__bridge id)CFGetSomething(); + [obj3 self]; // Add a use, to show we can use the object after it has been bridged. + id obj4 = (__bridge NSString*)CFGetString(); // expected-warning{{never read}} + id obj5 = (__bridge id)CFCreateSomething(); // expected-warning{{never read}} expected-warning{{leak}} + id obj6 = (__bridge NSString*)CFCreateString(); // expected-warning{{never read}} expected-warning{{leak}} +} + +void to_cf(id obj) { + CFTypeRef cf1 = (__bridge_retained CFTypeRef)CreateSomething(); // expected-warning{{never read}} + CFStringRef cf2 = (__bridge_retained CFStringRef)CreateNSString(); // expected-warning{{never read}} + CFTypeRef cf3 = (__bridge CFTypeRef)CreateSomething(); // expected-warning{{never read}} + CFStringRef cf4 = (__bridge CFStringRef)CreateNSString(); // expected-warning{{never read}} +} + +void test_objc_retainedObject() { + CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); + CFDateRef date = CFDateCreate(0, t); + id x = objc_retainedObject(date); + (void) x; +} + +void test_objc_unretainedObject() { + CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); + CFDateRef date = CFDateCreate(0, t); // expected-warning {{Potential leak}} + id x = objc_unretainedObject(date); + (void) x; +} + diff --git a/test/Analysis/pr4209.m b/test/Analysis/pr4209.m index 1e0fd32fcd..e3bc5cc143 100644 --- a/test/Analysis/pr4209.m +++ b/test/Analysis/pr4209.m @@ -49,17 +49,17 @@ CMProfileLocation; GSEbayCategory *rootCategory; } - (NSMutableDictionary*)categoryDictionaryForCategoryID:(int)inID inRootTreeCategories:(NSMutableArray*)inRootTreeCategories; // expected-note {{method definition for 'categoryDictionaryForCategoryID:inRootTreeCategories:' not found}} --(NSString*) categoryID; // expected-note {{method definition for 'categoryID' not found}} +-(NSString*) categoryID; // expected-note {{method definition for 'categoryID' not found}} expected-note {{using}} @end @interface GSEbayCategory : NSObject { } -- (int) categoryID; +- (int) categoryID; // expected-note {{also found}} - (GSEbayCategory *) parent; - (GSEbayCategory*) subcategoryWithID:(int) inID; @end @implementation GBCategoryChooserPanelController + (int) chooseCategoryIDFromCategories:(NSArray*) inCategories searchRequest:(GBSearchRequest*)inRequest parentWindow:(NSWindow*) inParent { // expected-warning {{incomplete implementation}} return 0; } - (void) addCategory:(EBayCategoryType*)inCategory toRootTreeCategory:(NSMutableArray*)inRootTreeCategories { - GSEbayCategory *category = [rootCategory subcategoryWithID:[[inCategory categoryID] intValue]]; + GSEbayCategory *category = [rootCategory subcategoryWithID:[[inCategory categoryID] intValue]]; // expected-warning {{multiple methods named 'categoryID' found}} if (rootCategory != category) { GSEbayCategory *parent = category; diff --git a/test/CodeGenObjC/arc-arm.m b/test/CodeGenObjC/arc-arm.m new file mode 100644 index 0000000000..a3d55c28f4 --- /dev/null +++ b/test/CodeGenObjC/arc-arm.m @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -triple armv7-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fblocks -fobjc-arc -o - %s | FileCheck %s + +id test0(void) { + extern id test0_helper(void); + // CHECK: [[T0:%.*]] = call arm_aapcscc i8* @test0_helper() + // CHECK-NEXT: ret i8* [[T0]] + return test0_helper(); +} + +void test1(void) { + extern id test1_helper(void); + // CHECK: [[T0:%.*]] = call arm_aapcscc i8* @test1_helper() + // CHECK-NEXT: call void asm sideeffect "mov\09r7, r7 + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) + // CHECK-NEXT: store i8* [[T1]], + // CHECK-NEXT: load + // CHECK-NEXT: call void @objc_release + // CHECK-NEXT: ret void + id x = test1_helper(); +} diff --git a/test/CodeGenObjC/arc-block-ivar-layout.m b/test/CodeGenObjC/arc-block-ivar-layout.m new file mode 100644 index 0000000000..b6a0427636 --- /dev/null +++ b/test/CodeGenObjC/arc-block-ivar-layout.m @@ -0,0 +1,60 @@ +// RUN: %clang_cc1 -fblocks -fobjc-arc -fobjc-nonfragile-abi -triple x86_64-apple-darwin -O0 -emit-llvm %s -o %t-64.s +// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s +// rdar://8991729 + +__weak id wid; +void x(id y) {} +void y(int a) {} + +extern id opaque_id(); + +void f() { + __block int byref_int = 0; + char ch = 'a'; + char ch1 = 'b'; + char ch2 = 'c'; + short sh = 2; + const id bar = (id) opaque_id(); + id baz = 0; + __strong id strong_void_sta; + __block id byref_bab = (id)0; + __block id bl_var1; + int i; double dob; + +// The patterns here are a sequence of bytes, each saying first how +// many sizeof(void*) chunks to skip (high nibble) and then how many +// to scan (low nibble). A zero byte says that we've reached the end +// of the pattern. +// +// All of these patterns start with 01 3x because the block header on +// LP64 consists of an isa pointer (which we're supposed to scan for +// some reason) followed by three words (2 ints, a function pointer, +// and a descriptor pointer). + +// Test 1 +// byref int, short, char, char, char, id, id, strong id, byref id +// 01 35 10 00 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [4 x i8] c"\015\10\00" + void (^b)() = ^{ + byref_int = sh + ch+ch1+ch2 ; + x(bar); + x(baz); + x((id)strong_void_sta); + x(byref_bab); + }; + b(); + +// Test 2 +// byref int, short, char, char, char, id, id, strong id, byref void*, byref id +// 01 36 10 00 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [4 x i8] c"\016\10\00" + void (^c)() = ^{ + byref_int = sh + ch+ch1+ch2 ; + x(bar); + x(baz); + x((id)strong_void_sta); + x(wid); + bl_var1 = 0; + x(byref_bab); + }; +} diff --git a/test/CodeGenObjC/arc-bridged-cast.m b/test/CodeGenObjC/arc-bridged-cast.m new file mode 100644 index 0000000000..3354bda5bf --- /dev/null +++ b/test/CodeGenObjC/arc-bridged-cast.m @@ -0,0 +1,90 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fblocks -fobjc-arc -O2 -disable-llvm-optzns -o - %s | FileCheck %s + +typedef const void *CFTypeRef; +typedef const struct __CFString *CFStringRef; + +@interface NSString +@end + +CFTypeRef CFCreateSomething(void); +CFStringRef CFCreateString(void); +CFTypeRef CFGetSomething(void); +CFStringRef CFGetString(void); + +id CreateSomething(void); +NSString *CreateNSString(void); + +// CHECK: define void @bridge_transfer_from_cf +void bridge_transfer_from_cf(int *i) { + // CHECK: store i32 7 + *i = 7; + // CHECK: call i8* @CFCreateSomething() + id obj1 = (__bridge_transfer id)CFCreateSomething(); + // CHECK-NOT: retain + // CHECK: store i32 11 + *i = 11; + // CHECK: call i8* @CFCreateSomething() + // CHECK-NOT: retain + // CHECK: store i32 13 + (void)(__bridge_transfer id)CFCreateSomething(), *i = 13; + // CHECK: call void @objc_release + // CHECK: store i32 17 + *i = 17; + // CHECK: call void @objc_release + // CHECK-NEXT: ret void +} + +// CHECK: define void @bridge_from_cf +void bridge_from_cf(int *i) { + // CHECK: store i32 7 + *i = 7; + // CHECK: call i8* @CFCreateSomething() + id obj1 = (__bridge id)CFCreateSomething(); + // CHECK: objc_retainAutoreleasedReturnValue + // CHECK: store i32 11 + *i = 11; + // CHECK: call i8* @CFCreateSomething() + // CHECK-NOT: release + // CHECK: store i32 13 + (void)(__bridge id)CFCreateSomething(), *i = 13; + // CHECK: store i32 17 + *i = 17; + // CHECK: call void @objc_release + // CHECK-NEXT: ret void +} + +// CHECK: define void @bridge_retained_of_cf +void bridge_retained_of_cf(int *i) { + *i = 7; + // CHECK: call i8* @CreateSomething() + CFTypeRef cf1 = (__bridge_retained CFTypeRef)CreateSomething(); + // CHECK-NEXT: call i8* @objc_retainAutoreleasedReturnValue + // CHECK: store i32 11 + *i = 11; + // CHECK: call i8* @CreateSomething() + (__bridge_retained CFTypeRef)CreateSomething(), *i = 13; + // CHECK-NEXT: call i8* @objc_retainAutoreleasedReturnValue + // CHECK: store i32 13 + // CHECK: store i32 17 + *i = 17; + // CHECK-NEXT: ret void +} + +// CHECK: define void @bridge_of_cf +void bridge_of_cf(int *i) { + // CHECK: store i32 7 + *i = 7; + // CHECK: call i8* @CreateSomething() + CFTypeRef cf1 = (__bridge CFTypeRef)CreateSomething(); + // CHECK-NOT: retain + // CHECK: store i32 11 + *i = 11; + // CHECK: call i8* @CreateSomething + (__bridge CFTypeRef)CreateSomething(), *i = 13; + // CHECK: store i32 13 + // CHECK-NOT: release + // CHECK: store i32 17 + *i = 17; + // CHECK-NEXT: ret void +} + diff --git a/test/CodeGenObjC/arc-foreach.m b/test/CodeGenObjC/arc-foreach.m new file mode 100644 index 0000000000..00b0adf393 --- /dev/null +++ b/test/CodeGenObjC/arc-foreach.m @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -fblocks -fobjc-arc -fobjc-nonfragile-abi -triple x86_64-apple-darwin -O0 -emit-llvm %s -o %t-64.s +// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s +// rdar://9503326 + +typedef void (^dispatch_block_t)(void); + +@class NSString; +extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1, 2))); +@class NSArray; + +int main (int argc, const char * argv[]) +{ + NSArray *array; + for ( NSString *str in array) { + dispatch_block_t blk = ^{ + NSLog(@"str in block: %@", str); + }; + blk(); + } + return 0; +} + +// CHECK-LP64: define internal void @__main_block_invoke +// CHECK-LP64: [[BLOCK:%.*]] = bitcast i8* {{%.*}} to [[BLOCK_T:%.*]]* +// CHECK-LP64-NEXT: [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 +// CHECK-LP64-NEXT: [[T2:%.*]] = load [[OPAQUE_T:%.*]]** [[T0]], align 8 +// CHECK-LP64-NEXT: call void ([[OPAQUE_T]]*, ...)* @NSLog diff --git a/test/CodeGenObjC/arc-ivar-layout.m b/test/CodeGenObjC/arc-ivar-layout.m new file mode 100644 index 0000000000..93c9ff7fed --- /dev/null +++ b/test/CodeGenObjC/arc-ivar-layout.m @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -fobjc-arc -fobjc-nonfragile-abi -triple x86_64-apple-darwin -O0 -S %s -o %t-64.s +// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s +// rdar://8991729 + +@interface NSObject { + id isa; +} +@end + +@interface AllPointers : NSObject { + id foo; + id __strong bar; + NSObject *bletch; +} +@end + +@implementation AllPointers +@end +// CHECK-LP64: L_OBJC_CLASS_NAME_1: +// CHECK-LP64-NEXT: .asciz "\003" + +@class NSString, NSNumber; +@interface A : NSObject { + NSString *foo; + NSNumber *bar; + unsigned int bletch; + __weak id delegate; +} +@end + +@interface B : A { + unsigned int x; + NSString *y; + NSString *z; +} +@end + +@implementation A @end + +@implementation B @end + +// CHECK-LP64: L_OBJC_CLASS_NAME_15: +// CHECK-LP64-NEXT: .asciz "\022" + diff --git a/test/CodeGenObjC/arc-no-runtime.m b/test/CodeGenObjC/arc-no-runtime.m new file mode 100644 index 0000000000..db1e42c448 --- /dev/null +++ b/test/CodeGenObjC/arc-no-runtime.m @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-arc -fobjc-nonfragile-abi -fobjc-no-arc-runtime -emit-llvm %s -o - | FileCheck %s + +// rdar://problem/9224855 +void test0() { + id x = 0; + // CHECK: call void @objc_release( +} + +// CHECK: declare extern_weak void @objc_release( diff --git a/test/CodeGenObjC/arc-related-result-type.m b/test/CodeGenObjC/arc-related-result-type.m new file mode 100644 index 0000000000..c51757df5d --- /dev/null +++ b/test/CodeGenObjC/arc-related-result-type.m @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fblocks -fobjc-arc -o - %s | FileCheck %s + +@interface Test0 +- (id) self; +@end +void test0(Test0 *val) { + Test0 *x = [val self]; + +// CHECK: define void @test0( +// CHECK: [[VAL:%.*]] = alloca [[TEST0:%.*]]* +// CHECK-NEXT: [[X:%.*]] = alloca [[TEST0]]* +// CHECK-NEXT: bitcast +// CHECK-NEXT: call i8* @objc_retain( +// CHECK-NEXT: bitcast +// CHECK-NEXT: store +// CHECK-NEXT: load [[TEST0]]** [[VAL]], +// CHECK-NEXT: load +// CHECK-NEXT: bitcast +// CHECK-NEXT: [[T0:%.*]] = call i8* bitcast ( +// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) +// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to [[TEST0]]* +// CHECK-NEXT: store [[TEST0]]* [[T2]], [[TEST0]]** [[X]] +// CHECK-NEXT: [[T0:%.*]] = load [[TEST0]]** [[X]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST0]]* [[T0]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T1]]) +// CHECK-NEXT: [[T0:%.*]] = load [[TEST0]]** [[VAL]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST0]]* [[T0]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T1]]) +// CHECK-NEXT: ret void +} diff --git a/test/CodeGenObjC/arc-unopt.m b/test/CodeGenObjC/arc-unopt.m new file mode 100644 index 0000000000..73d110c948 --- /dev/null +++ b/test/CodeGenObjC/arc-unopt.m @@ -0,0 +1,69 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fblocks -fobjc-arc -o - %s | FileCheck %s + +// A test to ensure that we generate fused calls at -O0. + +@class Test0; +Test0 *test0(void) { + extern Test0 *test0_helper; + return test0_helper; + + // CHECK: [[LD:%.*]] = load [[TEST0:%.*]]** @test0_helper + // CHECK-NEXT: [[T0:%.*]] = bitcast [[TEST0]]* [[LD]] to i8* + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleaseReturnValue(i8* [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to [[TEST0]]* + // CHECK-NEXT: ret [[TEST0]]* [[T2]] +} + +id test1(void) { + extern id test1_helper; + return test1_helper; + + // CHECK: [[LD:%.*]] = load i8** @test1_helper + // CHECK-NEXT: [[T0:%.*]] = call i8* @objc_retainAutoreleaseReturnValue(i8* [[LD]]) + // CHECK-NEXT: ret i8* [[T0]] +} + +void test2(void) { + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: store i8* null, i8** [[X]] + // CHECK-NEXT: call void @objc_destroyWeak(i8** [[X]]) + // CHECK-NEXT: ret void + __weak id x; +} + +id test3(void) { + extern id test3_helper(void); + // CHECK: [[T0:%.*]] = call i8* @test3_helper() + // CHECK-NEXT: ret i8* [[T0]] + return test3_helper(); +} + +@interface Test4 { id x; } @end +@interface Test4_sub : Test4 { id y; } @end +Test4 *test4(void) { + extern Test4_sub *test4_helper(void); + // CHECK: [[T0:%.*]] = call [[TEST4S:%.*]]* @test4_helper() + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST4S]]* [[T0]] to [[TEST4:%.*]]* + // CHECK-NEXT: ret [[TEST4]]* [[T1]] + return test4_helper(); +} + +// rdar://problem/9418404 +@class Test5; +void test5(void) { + Test5 *x, *y; + if ((x = y)) + y = 0; + +// CHECK: define void @test5() +// CHECK: [[X:%.*]] = alloca [[TEST5:%.*]]*, +// CHECK-NEXT: [[Y:%.*]] = alloca [[TEST5:%.*]]*, +// CHECK-NEXT: store [[TEST5]]* null, [[TEST5]]** [[X]], +// CHECK-NEXT: store [[TEST5]]* null, [[TEST5]]** [[Y]], +// CHECK-NEXT: [[T0:%.*]] = load [[TEST5]]** [[Y]], +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST5]]** [[X]] to i8** +// CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST5]]* [[T0]] to i8* +// CHECK-NEXT: call void @objc_storeStrong(i8** [[T1]], i8* [[T2]]) +// CHECK-NEXT: [[T3:%.*]] = icmp ne [[TEST5]]* [[T0]], null +// CHECK-NEXT: br i1 [[T3]], +} diff --git a/test/CodeGenObjC/arc-weak-property.m b/test/CodeGenObjC/arc-weak-property.m new file mode 100644 index 0000000000..2e274db198 --- /dev/null +++ b/test/CodeGenObjC/arc-weak-property.m @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fblocks -fobjc-arc -o - %s | FileCheck %s +// rdar://8899430 + +@interface WeakPropertyTest { + __weak id PROP; +} +@property () __weak id PROP; +@end + +@implementation WeakPropertyTest +@synthesize PROP; +@end + +// CHECK: define internal i8* @"\01-[WeakPropertyTest PROP]" +// CHECK: [[SELF:%.*]] = alloca [[WPT:%.*]]*, +// CHECK-NEXT: [[CMD:%.*]] = alloca i8*, +// CHECK-NEXT: store [[WPT]]* {{%.*}}, [[WPT]]** [[SELF]] +// CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]] +// CHECK-NEXT: [[T0:%.*]] = load [[WPT]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = load i64* @"OBJC_IVAR_$_WeakPropertyTest.PROP" +// CHECK-NEXT: [[T2:%.*]] = bitcast [[WPT]]* [[T0]] to i8* +// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds i8* [[T2]], i64 [[T1]] +// CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to i8** +// CHECK-NEXT: [[T5:%.*]] = call i8* @objc_loadWeakRetained(i8** [[T4]]) +// CHECK-NEXT: [[T6:%.*]] = call i8* @objc_autoreleaseReturnValue(i8* [[T5]]) +// CHECK-NEXT: ret i8* [[T6]] + +// CHECK: define internal void @"\01-[WeakPropertyTest setPROP:]" +// CHECK: [[SELF:%.*]] = alloca [[WPT:%.*]]*, +// CHECK-NEXT: [[CMD:%.*]] = alloca i8*, +// CHECK-NEXT: [[PROP:%.*]] = alloca i8*, +// CHECK-NEXT: store [[WPT]]* {{%.*}}, [[WPT]]** [[SELF]] +// CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]] +// CHECK-NEXT: store i8* {{%.*}}, i8** [[PROP]] +// CHECK-NEXT: [[V:%.*]] = load i8** [[PROP]] +// CHECK-NEXT: [[T0:%.*]] = load [[WPT]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = load i64* @"OBJC_IVAR_$_WeakPropertyTest.PROP" +// CHECK-NEXT: [[T2:%.*]] = bitcast [[WPT]]* [[T0]] to i8* +// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds i8* [[T2]], i64 [[T1]] +// CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to i8** +// CHECK-NEXT: call i8* @objc_storeWeak(i8** [[T4]], i8* [[V]]) +// CHECK-NEXT: ret void + +// CHECK: define internal void @"\01-[WeakPropertyTest .cxx_destruct]" +// CHECK: [[SELF:%.*]] = alloca [[WPT:%.*]]*, +// CHECK-NEXT: [[CMD:%.*]] = alloca i8*, +// CHECK-NEXT: store [[WPT]]* {{%.*}}, [[WPT]]** [[SELF]] +// CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]] +// CHECK-NEXT: [[T0:%.*]] = load [[WPT]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = load i64* @"OBJC_IVAR_$_WeakPropertyTest.PROP" +// CHECK-NEXT: [[T2:%.*]] = bitcast [[WPT]]* [[T0]] to i8* +// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds i8* [[T2]], i64 [[T1]] +// CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to i8** +// CHECK-NEXT: call void @objc_destroyWeak(i8** [[T4]]) +// CHECK-NEXT: ret void diff --git a/test/CodeGenObjC/arc.m b/test/CodeGenObjC/arc.m new file mode 100644 index 0000000000..1d5b4f4220 --- /dev/null +++ b/test/CodeGenObjC/arc.m @@ -0,0 +1,1511 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fblocks -fobjc-arc -O2 -disable-llvm-optzns -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fblocks -fobjc-arc -o - %s | FileCheck -check-prefix=CHECK-GLOBALS %s + +// CHECK: define void @test0 +void test0(id x) { + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: [[PARM:%.*]] = call i8* @objc_retain(i8* {{.*}}) + // CHECK-NEXT: store i8* [[PARM]], i8** [[X]] + // CHECK-NEXT: [[TMP:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[TMP]]) + // CHECK-NEXT: ret void +} + +// CHECK: define i8* @test1(i8* +id test1(id x) { + // CHECK: [[RET:%.*]] = alloca i8* + // CHECK-NEXT: [[X:%.*]] = alloca i8* + // CHECK-NEXT: [[Y:%.*]] = alloca i8* + // CHECK-NEXT: alloca i32 + // CHECK-NEXT: [[PARM:%.*]] = call i8* @objc_retain(i8* {{%.*}}) + // CHECK-NEXT: store i8* [[PARM]], i8** [[X]] + // CHECK-NEXT: store i8* null, i8** [[Y]] + // CHECK-NEXT: [[T0:%.*]] = load i8** [[Y]] + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) + // CHECK-NEXT: store i8* [[T1]], i8** [[RET]] + // CHECK-NEXT: store i32 + // CHECK-NEXT: [[T0:%.*]] = load i8** [[Y]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) + // CHECK-NEXT: [[T1:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + // CHECK-NEXT: [[T0:%.*]] = load i8** [[RET]] + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_autoreleaseReturnValue(i8* [[T0]]) + // CHECK-NEXT: ret i8* [[T1]] + id y; + return y; +} + +@interface Test2 ++ (void) class_method; +- (void) inst_method; +@end +@implementation Test2 + +// The self pointer of a class method is not retained. +// CHECK: define internal void @"\01+[Test2 class_method]" +// CHECK: alloca +// CHECK-NEXT: alloca +// CHECK-NEXT: store +// CHECK-NEXT: store +// CHECK-NEXT: ret void ++ (void) class_method {} + +// The self pointer of an instance method is not retained. +// CHECK: define internal void @"\01-[Test2 inst_method]" +// CHECK: alloca +// CHECK-NEXT: alloca +// CHECK-NEXT: store +// CHECK-NEXT: store +// CHECK-NEXT: ret void +- (void) inst_method {} +@end + +@interface Test3 ++ (id) alloc; +- (id) initWith: (int) x; +- (id) copy; +@end + +// CHECK: define void @test3_unelided() +void test3_unelided() { + extern void test3_helper(void); + + // CHECK: [[X:%.*]] = alloca [[TEST3:%.*]]* + // CHECK-NEXT: store [[TEST3]]* null, [[TEST3]]** [[X]], align + Test3 *x; + + // Call to +alloc. + // CHECK-NEXT: load {{.*}}* @"\01L_OBJC_CLASSLIST_REFERENCES_ + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: [[ALLOC:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend + // CHECK-NEXT: bitcast + // CHECK-NEXT: bitcast + // CHECK-NEXT: call void @objc_release(i8* + [Test3 alloc]; + + // CHECK-NEXT: [[T0:%.*]] = load [[TEST3]]** [[X]] + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST3]]* [[T0]] to i8* + // CHECK-NEXT: [[COPY:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend {{.*}})(i8* [[T1]], + // CHECK-NEXT: call void @objc_release(i8* [[COPY]]) nounwind + [x copy]; + + // CHECK-NEXT: [[T0:%.*]] = load [[TEST3]]** [[X]] + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST3]]* [[T0]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind + // CHECK-NEXT: ret void +} + +// CHECK: define void @test3() +void test3() { + // CHECK: [[X:%.*]] = alloca i8* + + id x = [[Test3 alloc] initWith: 5]; + + // Call to +alloc. + // CHECK-NEXT: load {{.*}}* @"\01L_OBJC_CLASSLIST_REFERENCES_ + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: [[ALLOC:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend + // CHECK-NEXT: bitcast + + // Call to -initWith: with elided retain of consumed argument. + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: [[INIT:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*, i32)*)(i8* + // CHECK-NEXT: bitcast + // CHECK-NEXT: [[INIT:%.*]] = bitcast + // Assignment for initialization, retention elided. + // CHECK-NEXT: store i8* [[INIT]], i8** [[X]] + + // Call to -copy. + // CHECK-NEXT: [[V:%.*]] = load i8** [[X]] + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: [[COPY:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend {{.*}})(i8* [[V]], + + // Assignment to x. + // CHECK-NEXT: [[TMP:%.*]] = load i8** [[X]] + // CHECK-NEXT: store i8* [[COPY]], i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[TMP]]) nounwind + + x = [x copy]; + + // Cleanup for x. + // CHECK-NEXT: [[TMP:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[TMP]]) nounwind + + // CHECK-NEXT: ret void +} + +// CHECK: define i8* @test4() +id test4() { + // Call to +alloc. + // CHECK: load {{.*}}* @"\01L_OBJC_CLASSLIST_REFERENCES_ + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: [[ALLOC:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend + // CHECK-NEXT: [[ALLOC:%.*]] = bitcast + + // Call to -initWith: with elided retain of consumed argument. + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: [[ALLOC:%.*]] = bitcast + // CHECK-NEXT: [[INIT:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*, i32)*)(i8* [[ALLOC]], + + // Initialization of return value, occuring within full-expression. + // Retain/release elided. + // CHECK-NEXT: bitcast + // CHECK-NEXT: [[INIT:%.*]] = bitcast + // CHECK-NEXT: [[RET:%.*]] = call i8* @objc_autoreleaseReturnValue(i8* [[INIT]]) + + // CHECK-NEXT: ret i8* [[RET]] + + return [[Test3 alloc] initWith: 6]; +} + +@interface Test5 { +@public + id var; +} +@end + +// CHECK: define void @test5 +void test5(Test5 *x, id y) { + // Prologue. + // CHECK: [[X:%.*]] = alloca [[TEST5:%.*]]*, + // CHECK-NEXT: [[Y:%.*]] = alloca i8* + // CHECK-NEXT: bitcast [[TEST5]]* {{%.*}} to i8* + // CHECK-NEXT: call i8* @objc_retain + // CHECK-NEXT: [[PARMX:%.*]] = bitcast i8* {{%.*}} to [[TEST5]]* + // CHECK-NEXT: store [[TEST5]]* [[PARMX]], [[TEST5]]** [[X]] + // CHECK-NEXT: call i8* @objc_retain + // CHECK-NEXT: store + + // CHECK-NEXT: load [[TEST5]]** [[X]] + // CHECK-NEXT: load i64* @"OBJC_IVAR_$_Test5.var" + // CHECK-NEXT: bitcast + // CHECK-NEXT: getelementptr + // CHECK-NEXT: [[VAR:%.*]] = bitcast + // CHECK-NEXT: [[TMP:%.*]] = load i8** [[VAR]] + // CHECK-NEXT: store i8* null, i8** [[VAR]] + // CHECK-NEXT: call void @objc_release(i8* [[TMP]]) nounwind + x->var = 0; + + // CHECK-NEXT: [[YVAL:%.*]] = load i8** [[Y]] + // CHECK-NEXT: load [[TEST5]]** [[X]] + // CHECK-NEXT: load i64* @"OBJC_IVAR_$_Test5.var" + // CHECK-NEXT: bitcast + // CHECK-NEXT: getelementptr + // CHECK-NEXT: [[VAR:%.*]] = bitcast + // CHECK-NEXT: [[T0:%.*]] = call i8* @objc_retain(i8* [[YVAL]]) nounwind + // CHECK-NEXT: [[TMP:%.*]] = load i8** [[VAR]] + // CHECK-NEXT: store i8* [[T0]], i8** [[VAR]] + // CHECK-NEXT: call void @objc_release(i8* [[TMP]]) nounwind + x->var = y; + + // Epilogue. + // CHECK-NEXT: [[TMP:%.*]] = load i8** [[Y]] + // CHECK-NEXT: call void @objc_release(i8* [[TMP]]) nounwind + // CHECK-NEXT: [[T0:%.*]] = load [[TEST5]]** [[X]] + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST5]]* [[T0]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind + // CHECK-NEXT: ret void +} + +id test6_helper(void) __attribute__((ns_returns_retained)); +// CHECK: define void @test6() +void test6() { + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: [[CALL:%.*]] = call i8* @test6_helper() + // CHECK-NEXT: store i8* [[CALL]], i8** [[X]] + // CHECK-NEXT: [[T1:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind, !clang.imprecise_release + // CHECK-NEXT: ret void + id x = test6_helper(); +} + +void test7_helper(id __attribute__((ns_consumed))); +// CHECK: define void @test7() +void test7() { + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: store i8* null, i8** [[X]] + // CHECK-NEXT: [[T0:%.*]] = load i8** [[X]] + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) nounwind + // CHECK-NEXT: call void @test7_helper(i8* [[T1]]) + // CHECK-NEXT: [[T1:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind, !clang.imprecise_release + // CHECK-NEXT: ret void + id x; + test7_helper(x); +} + +id test8_helper(void) __attribute__((ns_returns_retained)); +void test8() { + __unsafe_unretained id x = test8_helper(); + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: [[T0:%.*]] = call i8* @test8_helper() + // CHECK-NEXT: store i8* [[T0]], i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind + // CHECK-NOT: imprecise_release + // CHECK-NEXT: ret void +} + +id test9_helper(void) __attribute__((ns_returns_retained)); +void test9() { + id x __attribute__((objc_precise_lifetime)) = test9_helper(); + x = 0; + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: [[CALL:%.*]] = call i8* @test9_helper() + // CHECK-NEXT: store i8* [[CALL]], i8** [[X]] + + // CHECK-NEXT: [[T1:%.*]] = load i8** [[X]] + // CHECK-NEXT: store i8* null, i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind, !clang.imprecise_release + + // CHECK-NEXT: [[T1:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind + // CHECK-NOT: clang.imprecise_release + + // CHECK-NEXT: ret void +} + +@interface Test10 +@property (retain) Test10 *me; +@end +void test10() { + Test10 *x; + id y = x.me.me; + + // CHECK: define void @test10() + // CHECK: [[X:%.*]] = alloca [[TEST10:%.*]]*, align + // CHECK-NEXT: [[Y:%.*]] = alloca i8*, align + // CHECK-NEXT: store [[TEST10]]* null, [[TEST10]]** [[X]] + // CHECK-NEXT: load [[TEST10]]** [[X]], align + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_{{[0-9]*}}" + // CHECK-NEXT: bitcast + // CHECK-NEXT: call [[TEST10]]* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_{{[0-9]*}}" + // CHECK-NEXT: bitcast + // CHECK-NEXT: [[T0:%.*]] = call [[TEST10]]* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST10]]* [[T0]] to i8* + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]]) + // CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[TEST10]]* + // CHECK-NEXT: [[T4:%.*]] = bitcast [[TEST10]]* [[T3]] to i8* + // CHECK-NEXT: store i8* [[T4]], i8** [[Y]] + // CHECK-NEXT: [[T0:%.*]] = load i8** [[Y]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) + // CHECK-NEXT: [[T0:%.*]] = load [[TEST10]]** [[X]] + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST10]]* [[T0]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + // CHECK-NEXT: ret void +} + +void test11(id (*f)(void) __attribute__((ns_returns_retained))) { + // CHECK: define void @test11( + // CHECK: [[F:%.*]] = alloca i8* ()*, align + // CHECK-NEXT: [[X:%.*]] = alloca i8*, align + // CHECK-NEXT: store i8* ()* {{%.*}}, i8* ()** [[F]], align + // CHECK-NEXT: [[T0:%.*]] = load i8* ()** [[F]], align + // CHECK-NEXT: [[T1:%.*]] = call i8* [[T0]]() + // CHECK-NEXT: store i8* [[T1]], i8** [[X]], align + // CHECK-NEXT: [[T3:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T3]]) nounwind, !clang.imprecise_release + // CHECK-NEXT: ret void + id x = f(); +} + +void test12(void) { + extern id test12_helper(void); + + // CHECK: define void @test12() + // CHECK: [[X:%.*]] = alloca i8*, align + // CHECK-NEXT: [[Y:%.*]] = alloca i8*, align + + __weak id x = test12_helper(); + // CHECK-NEXT: [[T0:%.*]] = call i8* @test12_helper() + // CHECK-NEXT: call i8* @objc_initWeak(i8** [[X]], i8* [[T0]]) + + x = test12_helper(); + // CHECK-NEXT: [[T1:%.*]] = call i8* @test12_helper() + // CHECK-NEXT: call i8* @objc_storeWeak(i8** [[X]], i8* [[T1]]) + + id y = x; + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_loadWeakRetained(i8** [[X]]) + // CHECK-NEXT: store i8* [[T2]], i8** [[Y]], align + + // CHECK-NEXT: [[T4:%.*]] = load i8** [[Y]] + // CHECK-NEXT: call void @objc_release(i8* [[T4]]) nounwind, !clang.imprecise_release + // CHECK-NEXT: call void @objc_destroyWeak(i8** [[X]]) + // CHECK-NEXT: ret void +} + +// Indirect consuming calls. +void test13(void) { + // CHECK: define void @test13() + // CHECK: [[X:%.*]] = alloca i8*, align + // CHECK-NEXT: store i8* null, i8** [[X]], align + id x; + + typedef void fnty(id __attribute__((ns_consumed))); + extern fnty *test13_func; + // CHECK-NEXT: [[FN:%.*]] = load void (i8*)** @test13_func, align + // CHECK-NEXT: [[X_VAL:%.*]] = load i8** [[X]], align + // CHECK-NEXT: [[X_TMP:%.*]] = call i8* @objc_retain(i8* [[X_VAL]]) nounwind + // CHECK-NEXT: call void [[FN]](i8* [[X_TMP]]) + test13_func(x); + + extern fnty ^test13_block; + // CHECK-NEXT: [[TMP:%.*]] = load void (i8*)** @test13_block, align + // CHECK-NEXT: [[BLOCK:%.*]] = bitcast void (i8*)* [[TMP]] to [[BLOCKTY:%.*]]* + // CHECK-NEXT: [[BLOCK_FN_PTR:%.*]] = getelementptr inbounds [[BLOCKTY]]* [[BLOCK]], i32 0, i32 3 + // CHECK-NEXT: [[BLOCK_OPAQUE:%.*]] = bitcast [[BLOCKTY]]* [[BLOCK]] to i8* + // CHECK-NEXT: [[X_VAL:%.*]] = load i8** [[X]], align + // CHECK-NEXT: [[X_TMP:%.*]] = call i8* @objc_retain(i8* [[X_VAL]]) nounwind + // CHECK-NEXT: [[BLOCK_FN_TMP:%.*]] = load i8** [[BLOCK_FN_PTR]] + // CHECK-NEXT: [[BLOCK_FN:%.*]] = bitcast i8* [[BLOCK_FN_TMP]] to void (i8*, i8*)* + // CHECK-NEXT: call void [[BLOCK_FN]](i8* [[BLOCK_OPAQUE]], i8* [[X_TMP]]) + test13_block(x); + + // CHECK-NEXT: [[T0:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind + // CHECK-NEXT: ret void +} + +@interface Test16_super @end +@interface Test16 : Test16_super { + id z; +} +@property (assign) int x; +@property (retain) id y; +- (void) dealloc; +@end +@implementation Test16 +@synthesize x; +@synthesize y; +- (void) dealloc { + // CHECK: define internal void @"\01-[Test16 dealloc]"( + // CHECK: [[SELF:%.*]] = alloca [[TEST16:%.*]]*, align + // CHECK-NEXT: [[CMD:%.*]] = alloca i8*, align + // CHECK-NEXT: alloca + // CHECK-NEXT: store [[TEST16]]* {{%.*}}, [[TEST16]]** [[SELF]], align + // CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]] + // CHECK-NEXT: [[BASE:%.*]] = load [[TEST16]]** [[SELF]] + + // Call super. + // CHECK-NEXT: [[BASE2:%.*]] = bitcast [[TEST16]]* [[BASE]] to i8* + // CHECK-NEXT: [[T0:%.*]] = getelementptr + // CHECK-NEXT: store i8* [[BASE2]], i8** [[T0]] + // CHECK-NEXT: load {{%.*}}** @"\01L_OBJC_CLASSLIST_SUP_REFS_$_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: getelementptr + // CHECK-NEXT: store + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: call void bitcast (i8* ({{.*}})* @objc_msgSendSuper2 to void ( + // CHECK-NEXT: ret void +} + +// .cxx_destruct + // CHECK: define internal void @"\01-[Test16 .cxx_destruct]"( + // CHECK: [[SELF:%.*]] = alloca [[TEST16:%.*]]*, align + // CHECK-NEXT: [[CMD:%.*]] = alloca i8*, align + // CHECK-NEXT: store [[TEST16]]* {{%.*}}, [[TEST16]]** [[SELF]], align + // CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]] + // CHECK-NEXT: [[BASE:%.*]] = load [[TEST16]]** [[SELF]] + + // Destroy y. + // CHECK-NEXT: [[Y_OFF:%.*]] = load i64* @"OBJC_IVAR_$_Test16.y" + // CHECK-NEXT: [[T0:%.*]] = bitcast [[TEST16]]* [[BASE]] to i8* + // CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8* [[T0]], i64 [[Y_OFF]] + // CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to i8** + // CHECK-NEXT: call void @objc_storeStrong(i8** [[T2]], i8* null) nounwind + + // Destroy z. + // CHECK-NEXT: [[Z_OFF:%.*]] = load i64* @"OBJC_IVAR_$_Test16.z" + // CHECK-NEXT: [[T0:%.*]] = bitcast [[TEST16]]* [[BASE]] to i8* + // CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8* [[T0]], i64 [[Z_OFF]] + // CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to i8** + // CHECK-NEXT: call void @objc_storeStrong(i8** [[T2]], i8* null) nounwind + + // CHECK-NEXT: ret void + +@end + +// This shouldn't crash. +@interface Test17A +@property (assign) int x; +@end +@interface Test17B : Test17A +@end +@implementation Test17B +- (int) x { return super.x + 1; } +@end + +// This shouldn't crash. +void test18(id (^maker)(void)) { + maker(); +} + +void test19() { + // CHECK: define void @test19() + // CHECK: [[X:%.*]] = alloca [5 x i8*], align 16 + // CHECK-NEXT: [[T0:%.*]] = bitcast [5 x i8*]* [[X]] to i8* + // CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* [[T0]], i8 0, i64 40, i32 16, i1 false) + id x[5]; + + extern id test19_helper(void); + x[2] = test19_helper(); + + // CHECK-NEXT: [[CALL:%.*]] = call i8* @test19_helper() + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[CALL]]) nounwind + // CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds [5 x i8*]* [[X]], i32 0, i64 2 + // CHECK-NEXT: [[T0:%.*]] = load i8** [[SLOT]] + // CHECK-NEXT: store i8* [[T1]], i8** [[SLOT]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind + + // CHECK-NEXT: [[BEGIN:%.*]] = getelementptr inbounds [5 x i8*]* [[X]], i32 0, i32 0 + // CHECK-NEXT: [[END:%.*]] = getelementptr inbounds i8** [[BEGIN]], i64 5 + // CHECK-NEXT: br label + + // CHECK: [[CUR:%.*]] = phi i8** + // CHECK-NEXT: [[EQ:%.*]] = icmp eq i8** [[CUR]], [[END]] + // CHECK-NEXT: br i1 [[EQ]], + + // CHECK: [[T0:%.*]] = load i8** [[CUR]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind, !clang.imprecise_release + // CHECK-NEXT: [[NEXT:%.*]] = getelementptr inbounds i8** [[CUR]], i32 1 + // CHECK-NEXT: br label + + // CHECK: ret void +} + +void test20(unsigned n) { + // CHECK: define void @test20 + // CHECK: [[N:%.*]] = alloca i32, align 4 + // CHECK-NEXT: [[SAVED_STACK:%.*]] = alloca i8* + // CHECK-NEXT: store i32 {{%.*}}, i32* [[N]], align 4 + + id x[n]; + + // Capture the VLA size. + // CHECK-NEXT: [[T0:%.*]] = call i8* @llvm.stacksave() + // CHECK-NEXT: store i8* [[T0]], i8** [[SAVED_STACK]] + // CHECK-NEXT: [[T0:%.*]] = load i32* [[N]], align 4 + // CHECK-NEXT: [[T1:%.*]] = zext i32 [[T0]] to i64 + // CHECK-NEXT: [[VLA_SIZE:%.*]] = mul i64 8, [[T1]] + + // Allocate the VLA. + // CHECK-NEXT: [[T0:%.*]] = alloca i8, i64 [[VLA_SIZE]], align 16 + // CHECK-NEXT: [[VLA:%.*]] = bitcast i8* [[T0]] to i8** + + // Zero-initialize. + // CHECK-NEXT: [[T0:%.*]] = bitcast i8** [[VLA]] to i8* + // CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* [[T0]], i8 0, i64 [[VLA_SIZE]], i32 8, i1 false) + + // Destroy. + // CHECK-NEXT: [[VLA_COUNT:%.*]] = udiv i64 [[VLA_SIZE]], 8 + // CHECK-NEXT: [[END:%.*]] = getelementptr inbounds i8** [[VLA]], i64 [[VLA_COUNT]] + // CHECK-NEXT: br label + + // CHECK: [[CUR:%.*]] = phi i8** + // CHECK-NEXT: [[EQ:%.*]] = icmp eq i8** [[CUR]], [[END]] + // CHECK-NEXT: br i1 [[EQ]], + + // CHECK: [[T0:%.*]] = load i8** [[CUR]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind, !clang.imprecise_release + // CHECK-NEXT: [[NEXT:%.*]] = getelementptr inbounds i8** [[CUR]], i32 1 + // CHECK-NEXT: br label + + // CHECK: [[T0:%.*]] = load i8** [[SAVED_STACK]] + // CHECK-NEXT: call void @llvm.stackrestore(i8* [[T0]]) + // CHECK-NEXT: ret void +} + +void test21(unsigned n) { + // CHECK: define void @test21 + // CHECK: [[N:%.*]] = alloca i32, align 4 + // CHECK-NEXT: [[SAVED_STACK:%.*]] = alloca i8* + // CHECK-NEXT: store i32 {{%.*}}, i32* [[N]], align 4 + + id x[2][n][3]; + + // Capture the VLA size. + // CHECK-NEXT: [[T0:%.*]] = call i8* @llvm.stacksave() + // CHECK-NEXT: store i8* [[T0]], i8** [[SAVED_STACK]] + // CHECK-NEXT: [[T0:%.*]] = load i32* [[N]], align 4 + // CHECK-NEXT: [[T1:%.*]] = zext i32 [[T0]] to i64 + // CHECK-NEXT: [[T2:%.*]] = mul i64 24, [[T1]] + // CHECK-NEXT: [[VLA_SIZE:%.*]] = mul i64 [[T2]], 2 + + // Allocate the VLA. + // CHECK-NEXT: [[T0:%.*]] = alloca i8, i64 [[VLA_SIZE]], align 16 + // CHECK-NEXT: [[VLA:%.*]] = bitcast i8* [[T0]] to [3 x i8*]* + + // Zero-initialize. + // CHECK-NEXT: [[T0:%.*]] = bitcast [3 x i8*]* [[VLA]] to i8* + // CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* [[T0]], i8 0, i64 [[VLA_SIZE]], i32 8, i1 false) + + // Destroy. + // CHECK-NEXT: [[BEGIN:%.*]] = getelementptr inbounds [3 x i8*]* [[VLA]], i32 0, i32 0 + // CHECK-NEXT: [[VLA_COUNT:%.*]] = udiv i64 [[VLA_SIZE]], 8 + // CHECK-NEXT: [[END:%.*]] = getelementptr inbounds i8** [[BEGIN]], i64 [[VLA_COUNT]] + // CHECK-NEXT: br label + + // CHECK: [[CUR:%.*]] = phi i8** + // CHECK-NEXT: [[EQ:%.*]] = icmp eq i8** [[CUR]], [[END]] + // CHECK-NEXT: br i1 [[EQ]], + + // CHECK: [[T0:%.*]] = load i8** [[CUR]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind, !clang.imprecise_release + // CHECK-NEXT: [[NEXT:%.*]] = getelementptr inbounds i8** [[CUR]], i32 1 + // CHECK-NEXT: br label + + // CHECK: [[T0:%.*]] = load i8** [[SAVED_STACK]] + // CHECK-NEXT: call void @llvm.stackrestore(i8* [[T0]]) + // CHECK-NEXT: ret void +} + +void test22(_Bool cond) { + id test22_helper(void) __attribute__((ns_returns_retained)); + + // CHECK: define void @test22( + // CHECK: [[COND:%.*]] = alloca i8, + // CHECK-NEXT: [[X:%.*]] = alloca i8*, + // CHECK-NEXT: [[RELCOND:%.*]] = alloca i1 + // CHECK-NEXT: [[RELVAL:%.*]] = alloca i8* + // CHECK-NEXT: store i1 false, i1* [[RELCOND]] + // CHECK-NEXT: zext + // CHECK-NEXT: store + // CHECK-NEXT: [[T0:%.*]] = load i8* [[COND]] + // CHECK-NEXT: [[T1:%.*]] = trunc i8 [[T0]] to i1 + // CHECK-NEXT: br i1 [[T1]], + // CHECK: br label + // CHECK: [[CALL:%.*]] = call i8* @test22_helper() + // CHECK-NEXT: store i1 true, i1* [[RELCOND]] + // CHECK-NEXT: store i8* [[CALL]], i8** [[RELVAL]] + // CHECK-NEXT: br label + // CHECK: [[T0:%.*]] = phi i8* [ null, {{%.*}} ], [ [[CALL]], {{%.*}} ] + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) nounwind + // CHECK-NEXT: store i8* [[T1]], i8** [[X]], + // CHECK-NEXT: [[REL:%.*]] = load i1* [[RELCOND]] + // CHECK-NEXT: br i1 [[REL]], + // CHECK: [[T0:%.*]] = load i8** [[RELVAL]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind + // CHECK-NEXT: br label + // CHECK: [[T0:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind + // CHECK-NEXT: ret void + id x = (cond ? 0 : test22_helper()); +} + +// rdar://problem/8922540 +// Note that we no longer emit .release_ivars flags. +// CHECK-GLOBALS: @"\01l_OBJC_CLASS_RO_$_Test23" = internal global [[RO_T:%.*]] { i32 134, +@interface Test23 { id x; } @end +@implementation Test23 @end + +// CHECK-GLOBALS: @"\01l_OBJC_CLASS_RO_$_Test24" = internal global [[RO_T:%.*]] { i32 130, +@interface Test24 {} @end +@implementation Test24 @end + +int (^test25(int x))(void) { + // CHECK: define i32 ()* @test25( + // CHECK: [[X:%.*]] = alloca i32, + // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:%.*]], + // CHECK-NEXT: store i32 {{%.*}}, i32* [[X]] + // CHECK: [[T0:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]] to i32 ()* + // CHECK-NEXT: [[T1:%.*]] = bitcast i32 ()* [[T0]] to i8* + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainBlock(i8* [[T1]]) nounwind + // CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to i32 ()* + // CHECK-NEXT: [[T4:%.*]] = bitcast i32 ()* [[T3]] to i8* + // CHECK-NEXT: [[T5:%.*]] = call i8* @objc_autoreleaseReturnValue(i8* [[T4]]) nounwind + // CHECK-NEXT: [[T6:%.*]] = bitcast i8* [[T5]] to i32 ()* + // CHECK-NEXT: ret i32 ()* [[T6]] + return ^{ return x; }; +} + +// rdar://problem/8941012 +@interface Test26 { id x[4]; } @end +@implementation Test26 @end +// CHECK: define internal void @"\01-[Test26 .cxx_destruct]"( +// CHECK: [[SELF:%.*]] = load [[TEST26:%.*]]** +// CHECK-NEXT: [[OFFSET:%.*]] = load i64* @"OBJC_IVAR_$_Test26.x" +// CHECK-NEXT: [[T0:%.*]] = bitcast [[TEST26]]* [[SELF]] to i8* +// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8* [[T0]], i64 [[OFFSET]] +// CHECK-NEXT: [[X:%.*]] = bitcast i8* [[T1]] to [4 x i8*]* +// CHECK-NEXT: [[BEGIN:%.*]] = getelementptr inbounds [4 x i8*]* [[X]], i32 0, i32 0 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds i8** [[BEGIN]], i64 4 +// CHECK-NEXT: br label +// CHECK: [[CUR:%.*]] = phi i8** +// CHECK-NEXT: [[ISDONE:%.*]] = icmp eq i8** [[CUR]], [[END]] +// CHECK-NEXT: br i1 [[ISDONE]], +// CHECK: call void @objc_storeStrong(i8** [[CUR]], i8* null) +// CHECK-NEXT: [[NEXT:%.*]] = getelementptr inbounds i8** [[CUR]], i32 1 +// CHECK-NEXT: br label +// CHECK: ret void + +// Check that 'init' retains self. +@interface Test27 +- (id) init; +@end +@implementation Test27 +- (id) init { return self; } +// CHECK: define internal i8* @"\01-[Test27 init]" +// CHECK: [[RET:%.*]] = alloca i8*, +// CHECK-NEXT: [[SELF:%.*]] = alloca [[TEST27:%.*]]*, +// CHECK-NEXT: [[CMD:%.*]] = alloca i8*, +// CHECK-NEXT: [[DEST:%.*]] = alloca i32 +// CHECK-NEXT: store [[TEST27]]* {{%.*}}, [[TEST27]]** [[SELF]] +// CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]] +// CHECK-NEXT: [[T0:%.*]] = load [[TEST27]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST27]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] +// CHECK-NEXT: [[T2:%.*]] = bitcast +// CHECK-NEXT: store i8* [[T2]], i8** [[RET]] +// CHECK-NEXT: store i32 {{[0-9]+}}, i32* [[DEST]] +// CHECK-NEXT: [[T0:%.*]] = load [[TEST27]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST27]]* [[T0]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T1]]) +// CHECK-NEXT: [[T0:%.*]] = load i8** [[RET]] +// CHECK-NEXT: ret i8* [[T0]] + +@end + +// rdar://problem/8087194 +@interface Test28 +@property (copy) id prop; +@end +@implementation Test28 +@synthesize prop; +@end +// CHECK: define internal void @"\01-[Test28 .cxx_destruct]" +// CHECK: [[SELF:%.*]] = load [[TEST28:%.*]]** +// CHECK-NEXT: [[OFFSET:%.*]] = load i64* @"OBJC_IVAR_$_Test28.prop" +// CHECK-NEXT: [[T0:%.*]] = bitcast [[TEST28]]* [[SELF]] to i8* +// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8* [[T0]], i64 [[OFFSET]] +// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to i8** +// CHECK-NEXT: call void @objc_storeStrong(i8** [[T2]], i8* null) +// CHECK-NEXT: ret void + +@interface Test29_super +- (id) initWithAllocator: (id) allocator; +@end +@interface Test29 : Test29_super +- (id) init; +- (id) initWithAllocator: (id) allocator; +@end +@implementation Test29 +static id _test29_allocator = 0; +- (id) init { +// CHECK: define internal i8* @"\01-[Test29 init]"([[TEST29:%.*]]* {{%.*}}, +// CHECK: [[RET:%.*]] = alloca i8*, align 8 +// CHECK-NEXT: [[SELF:%.*]] = alloca [[TEST29]]*, align 8 +// CHECK-NEXT: [[CMD:%.*]] = alloca i8*, align 8 +// CHECK-NEXT: [[CLEANUP:%.*]] = alloca i32 +// CHECK-NEXT: store [[TEST29]]* {{%.*}}, [[TEST29]]** [[SELF]] +// CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]] + +// Evaluate arguments. Note that the send argument is evaluated +// before the zeroing of self. +// CHECK-NEXT: [[T0:%.*]] = load [[TEST29]]** [[SELF]], align 8 +// CHECK-NEXT: [[T1:%.*]] = load i8** @_test29_allocator, align 8 + +// Implicit null of 'self', i.e. direct transfer of ownership. +// CHECK-NEXT: store [[TEST29]]* null, [[TEST29]]** [[SELF]] + +// Actual message send. +// CHECK-NEXT: [[T2:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ +// CHECK-NEXT: [[T3:%.*]] = bitcast [[TEST29]]* [[T0]] to i8* +// CHECK-NEXT: [[CALL:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*, i8*)*)(i8* [[T3]], i8* [[T2]], i8* [[T1]]) + +// Implicit write of result back into 'self'. This is not supposed to +// be detectable because we're supposed to ban accesses to the old +// self value past the delegate init call. +// CHECK-NEXT: [[T0:%.*]] = bitcast i8* [[CALL]] to [[TEST29]]* +// CHECK-NEXT: store [[TEST29]]* [[T0]], [[TEST29]]** [[SELF]] + +// Return statement. +// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[CALL]] +// CHECK-NEXT: [[CALL:%.*]] = bitcast +// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[CALL]]) nounwind +// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] +// CHECK-NEXT: [[T1:%.*]] = bitcast +// CHECK-NEXT: store i8* [[T1]], i8** [[RET]] +// CHECK-NEXT: store i32 1, i32* [[CLEANUP]] + +// Cleanup. +// CHECK-NEXT: [[T0:%.*]] = load [[TEST29]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST29]]* [[T0]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind, !clang.imprecise_release + +// Return. +// CHECK-NEXT: [[T0:%.*]] = load i8** [[RET]] +// CHECK-NEXT: ret i8* [[T0]] + return [self initWithAllocator: _test29_allocator]; +} +- (id) initWithAllocator: (id) allocator { +// CHECK: define internal i8* @"\01-[Test29 initWithAllocator:]"( +// CHECK: [[RET:%.*]] = alloca i8*, align 8 +// CHECK-NEXT: [[SELF:%.*]] = alloca [[TEST29]]*, align 8 +// CHECK-NEXT: [[CMD:%.*]] = alloca i8*, align 8 +// CHECK-NEXT: [[ALLOCATOR:%.*]] = alloca i8*, align 8 +// CHECK-NEXT: alloca +// CHECK-NEXT: [[CLEANUP:%.*]] = alloca i32 +// CHECK-NEXT: store [[TEST29]]* {{%.*}}, [[TEST29]]** [[SELF]] +// CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]] +// CHECK-NEXT: [[T0:%.*]] = call i8* @objc_retain(i8* {{%.*}}) +// CHECK-NEXT: store i8* [[T0]], i8** [[ALLOCATOR]] + +// Evaluate arguments. Note that the send argument is evaluated +// before the zeroing of self. +// CHECK-NEXT: [[T0:%.*]] = load [[TEST29]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = load i8** [[ALLOCATOR]], align 8 + +// Implicit null of 'self', i.e. direct transfer of ownership. +// CHECK-NEXT: store [[TEST29]]* null, [[TEST29]]** [[SELF]] + +// Actual message send. +// CHECK: [[CALL:%.*]] = call {{.*}} @objc_msgSendSuper2 + +// Implicit write of result back into 'self'. This is not supposed to +// be detectable because we're supposed to ban accesses to the old +// self value past the delegate init call. +// CHECK-NEXT: [[T0:%.*]] = bitcast i8* [[CALL]] to [[TEST29]]* +// CHECK-NEXT: store [[TEST29]]* [[T0]], [[TEST29]]** [[SELF]] + +// Assignment. +// CHECK-NEXT: [[T0:%.*]] = bitcast i8* [[CALL]] to [[TEST29]]* +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST29]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) nounwind +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[TEST29]]* +// CHECK-NEXT: [[T4:%.*]] = load [[TEST29]]** [[SELF]], align +// CHECK-NEXT: store [[TEST29]]* [[T3]], [[TEST29]]** [[SELF]], align +// CHECK-NEXT: [[T5:%.*]] = bitcast [[TEST29]]* [[T4]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T5]]) + +// Return statement. +// CHECK-NEXT: [[T0:%.*]] = load [[TEST29]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST29]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) nounwind +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] +// CHECK-NEXT: [[T2:%.*]] = bitcast +// CHECK-NEXT: store i8* [[T2]], i8** [[RET]] +// CHECK-NEXT: store i32 1, i32* [[CLEANUP]] + +// Cleanup. +// CHECK-NEXT: [[T0:%.*]] = load i8** [[ALLOCATOR]] +// CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind, !clang.imprecise_release + +// CHECK-NEXT: [[T0:%.*]] = load [[TEST29]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST29]]* [[T0]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind, !clang.imprecise_release + +// Return. +// CHECK-NEXT: [[T0:%.*]] = load i8** [[RET]] +// CHECK-NEXT: ret i8* [[T0]] + self = [super initWithAllocator: allocator]; + return self; +} +@end + +typedef struct Test30_helper Test30_helper; +@interface Test30 +- (id) init; +- (Test30_helper*) initHelper; +@end +@implementation Test30 { +char *helper; +} +- (id) init { +// CHECK: define internal i8* @"\01-[Test30 init]"([[TEST30:%.*]]* {{%.*}}, +// CHECK: [[RET:%.*]] = alloca i8* +// CHECK-NEXT: [[SELF:%.*]] = alloca [[TEST30]]* +// CHECK-NEXT: alloca i8* +// CHECK-NEXT: alloca i32 +// CHECK-NEXT: store [[TEST30]]* {{%.*}}, [[TEST30]]** [[SELF]] +// CHECK-NEXT: store + +// Call. +// CHECK-NEXT: [[T0:%.*]] = load [[TEST30]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ +// CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST30]]* [[T0]] to i8* +// CHECK-NEXT: [[CALL:%.*]] = call [[TEST30_HELPER:%.*]]* bitcast {{.*}} @objc_msgSend {{.*}}(i8* [[T2]], i8* [[T1]]) + +// Assignment. +// CHECK-NEXT: [[T0:%.*]] = bitcast [[TEST30_HELPER]]* [[CALL]] to i8* +// CHECK-NEXT: [[T1:%.*]] = load [[TEST30]]** [[SELF]] +// CHECK-NEXT: [[IVAR:%.*]] = load i64* @"OBJC_IVAR_$_Test30.helper" +// CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST30]]* [[T1]] to i8* +// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds i8* [[T2]], i64 [[IVAR]] +// CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to i8** +// CHECK-NEXT#: [[T5:%.*]] = load i8** [[T4]] +// CHECK-NEXT#: [[T6:%.*]] = call i8* @objc_retain(i8* [[T0]]) +// CHECK-NEXT#: call void @objc_release(i8* [[T5]]) +// CHECK-NEXT: store i8* [[T0]], i8** [[T4]] + +// Return. +// CHECK-NEXT: [[T0:%.*]] = load [[TEST30]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST30]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] +// CHECK-NEXT: [[T2:%.*]] = bitcast +// CHECK-NEXT: store i8* [[T2]], i8** [[RET]] +// CHECK-NEXT: store i32 1 + +// Cleanup. +// CHECK-NEXT: [[T0:%.*]] = load [[TEST30]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST30]]* [[T0]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T1]]) + +// Epilogue. +// CHECK-NEXT: [[T0:%.*]] = load i8** [[RET]] +// CHECK-NEXT: ret i8* [[T0]] + self->helper = [self initHelper]; + return self; +} +- (Test30_helper*) initHelper { +// CHECK: define internal [[TEST30_HELPER]]* @"\01-[Test30 initHelper]"( +// CHECK: alloca +// CHECK-NEXT: alloca +// CHECK-NEXT: store +// CHECK-NEXT: store +// CHECK-NEXT: ret [[TEST30_HELPER]]* null + return 0; +} + +@end + +void test31(id x) { +// CHECK: define void @test31( +// CHECK: [[X:%.*]] = alloca i8*, +// CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:%.*]], +// CHECK-NEXT: [[PARM:%.*]] = call i8* @objc_retain(i8* {{%.*}}) +// CHECK-NEXT: store i8* [[PARM]], i8** [[X]] +// CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 +// CHECK-NEXT: [[T0:%.*]] = load i8** [[X]], +// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) +// CHECK-NEXT: store i8* [[T1]], i8** [[SLOT]], +// CHECK-NEXT: bitcast +// CHECK-NEXT: call void @test31_helper( +// CHECK-NEXT: [[T0:%.*]] = load i8** [[SLOT]] +// CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind, !clang.imprecise_release +// CHECK-NEXT: [[T0:%.*]] = load i8** [[X]] +// CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind, !clang.imprecise_release +// CHECK-NEXT: ret void + extern void test31_helper(id (^)(void)); + test31_helper(^{ return x; }); +} + +__attribute__((ns_returns_retained)) id test32(void) { +// CHECK: define i8* @test32() +// CHECK: [[CALL:%.*]] = call i8* @test32_helper() +// CHECK-NEXT: [[T0:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[CALL]]) +// CHECK-NEXT: ret i8* [[T0]] + extern id test32_helper(void); + return test32_helper(); +} + +@class Test33_a; +@interface Test33 +- (void) give: (Test33_a **) x; +- (void) take: (Test33_a **) x; +- (void) giveStrong: (out __strong Test33_a **) x; +- (void) takeStrong: (inout __strong Test33_a **) x; +- (void) giveOut: (out Test33_a **) x; +@end +void test33(Test33 *ptr) { + Test33_a *a; + [ptr give: &a]; + [ptr take: &a]; + [ptr giveStrong: &a]; + [ptr takeStrong: &a]; + [ptr giveOut: &a]; + + // CHECK: define void @test33([[TEST33:%.*]]* + // CHECK: [[PTR:%.*]] = alloca [[TEST33]]* + // CHECK-NEXT: [[A:%.*]] = alloca [[A_T:%.*]]* + // CHECK-NEXT: [[TEMP0:%.*]] = alloca [[A_T]]* + // CHECK-NEXT: [[TEMP1:%.*]] = alloca [[A_T]]* + // CHECK-NEXT: [[TEMP2:%.*]] = alloca [[A_T]]* + // CHECK-NEXT: bitcast + // CHECK-NEXT: objc_retain + // CHECK-NEXT: bitcast + // CHECK-NEXT: store + // CHECK-NEXT: store [[A_T]]* null, [[A_T]]** [[A]] + + // CHECK-NEXT: load [[TEST33]]** [[PTR]] + // CHECK-NEXT: [[T0:%.*]] = load [[A_T]]** [[A]] + // CHECK-NEXT: store [[A_T]]* [[T0]], [[A_T]]** [[TEMP0]] + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: objc_msgSend{{.*}}, [[A_T]]** [[TEMP0]]) + // CHECK-NEXT: [[T0:%.*]] = load [[A_T]]** [[TEMP0]] + // CHECK-NEXT: [[T1:%.*]] = bitcast [[A_T]]* [[T0]] to i8* + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) + // CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[A_T]]* + // CHECK-NEXT: [[T4:%.*]] = load [[A_T]]** [[A]] + // CHECK-NEXT: store [[A_T]]* [[T3]], [[A_T]]** [[A]] + // CHECK-NEXT: [[T5:%.*]] = bitcast [[A_T]]* [[T4]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T5]]) + + // CHECK-NEXT: load [[TEST33]]** [[PTR]] + // CHECK-NEXT: [[T0:%.*]] = load [[A_T]]** [[A]] + // CHECK-NEXT: store [[A_T]]* [[T0]], [[A_T]]** [[TEMP1]] + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: objc_msgSend{{.*}}, [[A_T]]** [[TEMP1]]) + // CHECK-NEXT: [[T0:%.*]] = load [[A_T]]** [[TEMP1]] + // CHECK-NEXT: [[T1:%.*]] = bitcast [[A_T]]* [[T0]] to i8* + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) + // CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[A_T]]* + // CHECK-NEXT: [[T4:%.*]] = load [[A_T]]** [[A]] + // CHECK-NEXT: store [[A_T]]* [[T3]], [[A_T]]** [[A]] + // CHECK-NEXT: [[T5:%.*]] = bitcast [[A_T]]* [[T4]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T5]]) + + // CHECK-NEXT: load [[TEST33]]** [[PTR]] + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: objc_msgSend{{.*}}, [[A_T]]** [[A]]) + + // CHECK-NEXT: load [[TEST33]]** [[PTR]] + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: objc_msgSend{{.*}}, [[A_T]]** [[A]]) + + // 'out' + // CHECK-NEXT: load [[TEST33]]** [[PTR]] + // CHECK-NEXT: store [[A_T]]* null, [[A_T]]** [[TEMP2]] + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: objc_msgSend{{.*}}, [[A_T]]** [[TEMP2]]) + // CHECK-NEXT: [[T0:%.*]] = load [[A_T]]** [[TEMP2]] + // CHECK-NEXT: [[T1:%.*]] = bitcast [[A_T]]* [[T0]] to i8* + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) + // CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[A_T]]* + // CHECK-NEXT: [[T4:%.*]] = load [[A_T]]** [[A]] + // CHECK-NEXT: store [[A_T]]* [[T3]], [[A_T]]** [[A]] + // CHECK-NEXT: [[T5:%.*]] = bitcast [[A_T]]* [[T4]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T5]]) + + // CHECK-NEXT: load + // CHECK-NEXT: bitcast + // CHECK-NEXT: objc_release + // CHECK-NEXT: load + // CHECK-NEXT: bitcast + // CHECK-NEXT: objc_release + // CHECK-NEXT: ret void +} + +void test34(int cond) { + __strong id strong; + __weak id weak; + extern void test34_sink(id *); + test34_sink(cond ? &strong : 0); + test34_sink(cond ? &weak : 0); + + // CHECK: define void @test34( + // CHECK: [[COND:%.*]] = alloca i32 + // CHECK-NEXT: [[STRONG:%.*]] = alloca i8* + // CHECK-NEXT: [[WEAK:%.*]] = alloca i8* + // CHECK-NEXT: [[TEMP1:%.*]] = alloca i8* + // CHECK-NEXT: [[TEMP2:%.*]] = alloca i8* + // CHECK-NEXT: store i32 + // CHECK-NEXT: store i8* null, i8** [[STRONG]] + // CHECK-NEXT: call i8* @objc_initWeak(i8** [[WEAK]], i8* null) + + // CHECK-NEXT: [[T0:%.*]] = load i32* [[COND]] + // CHECK-NEXT: [[T1:%.*]] = icmp ne i32 [[T0]], 0 + // CHECK: [[ARG:%.*]] = phi i8** + // CHECK-NEXT: [[T0:%.*]] = icmp eq i8** [[ARG]], null + // CHECK-NEXT: [[T1:%.*]] = select i1 [[T0]], i8** null, i8** [[TEMP1]] + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[T0:%.*]] = load i8** [[ARG]] + // CHECK-NEXT: store i8* [[T0]], i8** [[TEMP1]] + // CHECK-NEXT: br label + // CHECK: call void @test34_sink(i8** [[T1]]) + // CHECK-NEXT: [[T0:%.*]] = icmp eq i8** [[ARG]], null + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[T0:%.*]] = load i8** [[TEMP1]] + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = load i8** [[ARG]] + // CHECK-NEXT: store i8* [[T1]], i8** [[ARG]] + // CHECK-NEXT: call void @objc_release(i8* [[T2]]) + // CHECK-NEXT: br label + + // CHECK: [[T0:%.*]] = load i32* [[COND]] + // CHECK-NEXT: [[T1:%.*]] = icmp ne i32 [[T0]], 0 + // CHECK: [[ARG:%.*]] = phi i8** + // CHECK-NEXT: [[T0:%.*]] = icmp eq i8** [[ARG]], null + // CHECK-NEXT: [[T1:%.*]] = select i1 [[T0]], i8** null, i8** [[TEMP2]] + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[T0:%.*]] = call i8* @objc_loadWeak(i8** [[ARG]]) + // CHECK-NEXT: store i8* [[T0]], i8** [[TEMP2]] + // CHECK-NEXT: br label + // CHECK: call void @test34_sink(i8** [[T1]]) + // CHECK-NEXT: [[T0:%.*]] = icmp eq i8** [[ARG]], null + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[T0:%.*]] = load i8** [[TEMP2]] + // CHECK-NEXT: call i8* @objc_storeWeak(i8** [[ARG]], i8* [[T0]]) + // CHECK-NEXT: br label + + // CHECK: call void @objc_destroyWeak(i8** [[WEAK]]) + // CHECK: ret void +} + +void test35(void (^sink)(id*)) { + __strong id strong; + sink(&strong); + + // CHECK: define void @test35( + // CHECK: [[SINK:%.*]] = alloca void (i8**)* + // CHECK-NEXT: [[STRONG:%.*]] = alloca i8* + // CHECK-NEXT: [[TEMP:%.*]] = alloca i8* + // CHECK-NEXT: bitcast void (i8**)* {{%.*}} to i8* + // CHECK-NEXT: call i8* @objc_retain( + // CHECK-NEXT: bitcast i8* + // CHECK-NEXT: store void (i8**)* {{%.*}}, void (i8**)** [[SINK]] + // CHECK-NEXT: store i8* null, i8** [[STRONG]] + + // CHECK-NEXT: load void (i8**)** [[SINK]] + // CHECK-NEXT: bitcast + // CHECK-NEXT: getelementptr + // CHECK-NEXT: [[BLOCK:%.*]] = bitcast + // CHECK-NEXT: [[T0:%.*]] = load i8** [[STRONG]] + // CHECK-NEXT: store i8* [[T0]], i8** [[TEMP1]] + // CHECK-NEXT: [[F0:%.*]] = load i8** + // CHECK-NEXT: [[F1:%.*]] = bitcast i8* [[F0]] to void (i8*, i8**)* + // CHECK-NEXT: call void [[F1]](i8* [[BLOCK]], i8** [[TEMP1]]) + // CHECK-NEXT: [[T0:%.*]] = load i8** [[TEMP1]] + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = load i8** [[STRONG]] + // CHECK-NEXT: store i8* [[T1]], i8** [[STRONG]] + // CHECK-NEXT: call void @objc_release(i8* [[T2]]) + + // CHECK-NEXT: [[T0:%.*]] = load i8** [[STRONG]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) + + // CHECK-NEXT: load void (i8**)** [[SINK]] + // CHECK-NEXT: bitcast + // CHECK-NEXT: call void @objc_release + // CHECK-NEXT: ret void + +} + +// CHECK: define void @test36 +void test36(id x) { + // CHECK: [[X:%.*]] = alloca i8* + + // CHECK: call i8* @objc_retain + // CHECK: call i8* @objc_retain + // CHECK: call i8* @objc_retain + id array[3] = { @"A", x, @"y" }; + + // CHECK: [[T0:%.*]] = load i8** [[X]] + // CHECK-NEXT: store i8* null, i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) + x = 0; + + // CHECK: br label + // CHECK: call void @objc_release + // CHECK: br label + + // CHECK: call void @objc_release + // CHECK-NEXT: ret void +} + +@class Test37; +void test37(void) { + extern void test37_helper(id *); + Test37 *var; + test37_helper(&var); + + // CHECK: define void @test37() + // CHECK: [[VAR:%.*]] = alloca [[TEST37:%.*]]*, + // CHECK-NEXT: [[TEMP:%.*]] = alloca i8* + // CHECK-NEXT: store [[TEST37]]* null, [[TEST37]]** [[VAR]] + + // CHECK-NEXT: [[T0:%.*]] = load [[TEST37]]** [[VAR]] + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST37]]* [[T0]] to i8* + // CHECK-NEXT: store i8* [[T1]], i8** [[TEMP]] + // CHECK-NEXT: call void @test37_helper(i8** [[TEMP]]) + // CHECK-NEXT: [[T0:%.*]] = load i8** [[TEMP]] + // CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to [[TEST37]]* + // CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST37]]* [[T1]] to i8* + // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]]) + // CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to [[TEST37]]* + // CHECK-NEXT: [[T5:%.*]] = load [[TEST37]]** [[VAR]] + // CHECK-NEXT: store [[TEST37]]* [[T4]], [[TEST37]]** [[VAR]] + // CHECK-NEXT: [[T6:%.*]] = bitcast [[TEST37]]* [[T5]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T6]]) + + // CHECK-NEXT: [[T0:%.*]] = load [[TEST37]]** [[VAR]] + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST37]]* [[T0]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + // CHECK-NEXT: ret void +} + +void test38(void) { + id test38_source(void); + void test38_helper(void (^)(void)); + __block id var = test38_source(); + test38_helper(^{ var = 0; }); + + // CHECK: define void @test38() + // CHECK: [[VAR:%.*]] = alloca [[BYREF_T:%.*]], + // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:%.*]], + // CHECK: [[T0:%.*]] = getelementptr inbounds [[BYREF_T]]* [[VAR]], i32 0, i32 2 + // 0x02000000 - has copy/dispose helpers + // CHECK-NEXT: store i32 33554432, i32* [[T0]] + // CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BYREF_T]]* [[VAR]], i32 0, i32 6 + // CHECK-NEXT: [[T0:%.*]] = call i8* @test38_source() + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) + // CHECK-NEXT: store i8* [[T1]], i8** [[SLOT]] + // CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds [[BYREF_T]]* [[VAR]], i32 0, i32 6 + // 0x42000000 - has signature, copy/dispose helpers + // CHECK: store i32 1107296256, + // CHECK: [[T0:%.*]] = bitcast [[BYREF_T]]* [[VAR]] to i8* + // CHECK-NEXT: store i8* [[T0]], i8** + // CHECK: call void @test38_helper( + // CHECK: [[T0:%.*]] = bitcast [[BYREF_T]]* [[VAR]] to i8* + // CHECK-NEXT: call void @_Block_object_dispose(i8* [[T0]], i32 8) + // CHECK-NEXT: [[T0:%.*]] = load i8** [[SLOT]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) + // CHECK-NEXT: ret void + + // CHECK: define internal void @__Block_byref_object_copy_ + // CHECK: [[T0:%.*]] = getelementptr inbounds [[BYREF_T]]* {{%.*}}, i32 0, i32 6 + // CHECK-NEXT: load i8** + // CHECK-NEXT: bitcast i8* {{%.*}} to [[BYREF_T]]* + // CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[BYREF_T]]* {{%.*}}, i32 0, i32 6 + // CHECK-NEXT: [[T2:%.*]] = load i8** [[T1]] + // CHECK-NEXT: store i8* [[T2]], i8** [[T0]] + // CHECK-NEXT: store i8* null, i8** [[T1]] + + // CHECK: define internal void @__Block_byref_object_dispose_ + // CHECK: [[T0:%.*]] = getelementptr inbounds [[BYREF_T]]* {{%.*}}, i32 0, i32 6 + // CHECK-NEXT: [[T1:%.*]] = load i8** [[T0]] + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + + // CHECK: define internal void @__test38_block_invoke_ + // CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BYREF_T]]* {{%.*}}, i32 0, i32 6 + // CHECK-NEXT: [[T0:%.*]] = load i8** [[SLOT]], align 8 + // CHECK-NEXT: store i8* null, i8** [[SLOT]], + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) + // CHECK-NEXT: ret void + + // CHECK: define internal void @__copy_helper_block_ + // CHECK: call void @_Block_object_assign(i8* {{%.*}}, i8* {{%.*}}, i32 8) + + // CHECK: define internal void @__destroy_helper_block_ + // CHECK: call void @_Block_object_dispose(i8* {{%.*}}, i32 8) +} + +void test39(void) { + extern id test39_source(void); + void test39_helper(void (^)(void)); + __unsafe_unretained id var = test39_source(); + test39_helper(^{ (void) var; }); + + // CHECK: define void @test39() + // CHECK: [[VAR:%.*]] = alloca i8* + // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:%.*]], + // CHECK-NEXT: [[T0:%.*]] = call i8* @test39_source() + // CHECK-NEXT: store i8* [[T0]], i8** [[VAR]], + // 0x40000000 - has signature but no copy/dispose + // CHECK: store i32 1073741824, i32* + // CHECK: [[CAPTURE:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 + // CHECK-NEXT: [[T0:%.*]] = load i8** [[VAR]] + // CHECK-NEXT: store i8* [[T0]], i8** [[CAPTURE]] + // CHECK-NEXT: [[T0:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]] to void ()* + // CHECK-NEXT: call void @test39_helper(void ()* [[T0]]) + // CHECK-NEXT: ret void +} + +void test40(void) { + id test40_source(void); + void test40_helper(void (^)(void)); + __block __weak id var = test40_source(); + test40_helper(^{ var = 0; }); + + // CHECK: define void @test40() + // CHECK: [[VAR:%.*]] = alloca [[BYREF_T:%.*]], + // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:%.*]], + // CHECK: [[T0:%.*]] = getelementptr inbounds [[BYREF_T]]* [[VAR]], i32 0, i32 2 + // 0x02000000 - has copy/dispose helpers + // CHECK-NEXT: store i32 33554432, i32* [[T0]] + // CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BYREF_T]]* [[VAR]], i32 0, i32 6 + // CHECK-NEXT: [[T0:%.*]] = call i8* @test40_source() + // CHECK-NEXT: call i8* @objc_initWeak(i8** [[SLOT]], i8* [[T0]]) + // CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds [[BYREF_T]]* [[VAR]], i32 0, i32 6 + // 0x42000000 - has signature, copy/dispose helpers + // CHECK: store i32 1107296256, + // CHECK: [[T0:%.*]] = bitcast [[BYREF_T]]* [[VAR]] to i8* + // CHECK-NEXT: store i8* [[T0]], i8** + // CHECK: call void @test40_helper( + // CHECK: [[T0:%.*]] = bitcast [[BYREF_T]]* [[VAR]] to i8* + // CHECK-NEXT: call void @_Block_object_dispose(i8* [[T0]], i32 8) + // CHECK-NEXT: call void @objc_destroyWeak(i8** [[SLOT]]) + // CHECK-NEXT: ret void + + // CHECK: define internal void @__Block_byref_object_copy_ + // CHECK: [[T0:%.*]] = getelementptr inbounds [[BYREF_T]]* {{%.*}}, i32 0, i32 6 + // CHECK-NEXT: load i8** + // CHECK-NEXT: bitcast i8* {{%.*}} to [[BYREF_T]]* + // CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[BYREF_T]]* {{%.*}}, i32 0, i32 6 + // CHECK-NEXT: call void @objc_moveWeak(i8** [[T0]], i8** [[T1]]) + + // CHECK: define internal void @__Block_byref_object_dispose_ + // CHECK: [[T0:%.*]] = getelementptr inbounds [[BYREF_T]]* {{%.*}}, i32 0, i32 6 + // CHECK-NEXT: call void @objc_destroyWeak(i8** [[T0]]) + + // CHECK: define internal void @__test40_block_invoke_ + // CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BYREF_T]]* {{%.*}}, i32 0, i32 6 + // CHECK-NEXT: call i8* @objc_storeWeak(i8** [[SLOT]], i8* null) + // CHECK-NEXT: ret void + + // CHECK: define internal void @__copy_helper_block_ + // 0x8 - FIELD_IS_BYREF (no FIELD_IS_WEAK because clang in control) + // CHECK: call void @_Block_object_assign(i8* {{%.*}}, i8* {{%.*}}, i32 8) + + // CHECK: define internal void @__destroy_helper_block_ + // 0x8 - FIELD_IS_BYREF (no FIELD_IS_WEAK because clang in control) + // CHECK: call void @_Block_object_dispose(i8* {{%.*}}, i32 8) +} + +void test41(void) { + id test41_source(void); + void test41_helper(void (^)(void)); + void test41_consume(id); + __weak id var = test41_source(); + test41_helper(^{ test41_consume(var); }); + + // CHECK: define void @test41() + // CHECK: [[VAR:%.*]] = alloca i8*, + // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:%.*]], + // CHECK: [[T0:%.*]] = call i8* @test41_source() + // CHECK-NEXT: call i8* @objc_initWeak(i8** [[VAR]], i8* [[T0]]) + // 0x42000000 - has signature, copy/dispose helpers + // CHECK: store i32 1107296256, + // CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 + // CHECK-NEXT: [[T0:%.*]] = call i8* @objc_loadWeak(i8** [[VAR]]) + // CHECK-NEXT: call i8* @objc_initWeak(i8** [[SLOT]], i8* [[T0]]) + // CHECK: call void @test41_helper( + // CHECK-NEXT: call void @objc_destroyWeak(i8** [[SLOT]]) + // CHECK-NEXT: call void @objc_destroyWeak(i8** [[VAR]]) + // CHECK-NEXT: ret void + + // CHECK: define internal void @__test41_block_invoke_ + // CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BLOCK_T]]* {{%.*}}, i32 0, i32 5 + // CHECK-NEXT: [[T0:%.*]] = call i8* @objc_loadWeak(i8** [[SLOT]]) + // CHECK-NEXT: call void @test41_consume(i8* [[T0]]) + // CHECK-NEXT: ret void + + // CHECK: define internal void @__copy_helper_block_ + // CHECK: getelementptr + // CHECK-NEXT: getelementptr + // CHECK-NEXT: call void @objc_copyWeak( + + // CHECK: define internal void @__destroy_helper_block_ + // CHECK: getelementptr + // CHECK-NEXT: call void @objc_destroyWeak( +} + +@interface Test42 @end +@implementation Test42 +- (void) test { +// CHECK: define internal void @"\01-[Test42 test]" +// CHECK: [[SELF:%.*]] = alloca [[TEST42:%.*]]*, +// CHECK-NEXT: alloca i8* +// CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:%.*]], +// CHECK-NEXT: store +// CHECK-NEXT: store +// CHECK: [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 +// CHECK-NEXT: [[T1:%.*]] = load [[TEST42]]** [[SELF]], +// CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST42]]* [[T1]] to i8* +// CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]]) +// CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to [[TEST42]]* +// CHECK-NEXT: store [[TEST42]]* [[T4]], [[TEST42]]** [[T0]] +// CHECK-NEXT: bitcast [[BLOCK_T]]* [[BLOCK]] to void ()* +// CHECK-NEXT: call void @test42_helper( +// CHECK-NEXT: [[T1:%.*]] = load [[TEST42]]** [[T0]] +// CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST42]]* [[T1]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T2]]) +// CHECK-NEXT: ret void + + extern void test42_helper(void (^)(void)); + test42_helper(^{ (void) self; }); +} +@end + +@interface Test43 @end +@implementation Test43 +- (id) test __attribute__((ns_returns_retained)) { + extern id test43_produce(void); + return test43_produce(); + // CHECK: call i8* @test43_produce() + // CHECK-NEXT: call i8* @objc_retainAutoreleasedReturnValue( + // CHECK-NEXT: ret +} +@end + +id test44(void) { + typedef id __attribute__((ns_returns_retained)) blocktype(void); + extern test44_consume_block(blocktype^); + return ^blocktype { + extern id test44_produce(void); + return test44_produce(); + }(); + +// CHECK: define i8* @test44( +// CHECK: load i8** getelementptr +// CHECK-NEXT: bitcast i8* +// CHECK-NEXT: call i8* +// CHECK-NEXT: call i8* @objc_autoreleaseReturnValue +// CHECK-NEXT: ret i8* + +// CHECK: call i8* @test44_produce() +// CHECK-NEXT: call i8* @objc_retain +// CHECK-NEXT: ret i8* +} + +@interface Test45 +@property (retain) id x; +@end +@implementation Test45 +@synthesize x; +@end +// CHECK: define internal i8* @"\01-[Test45 x]"( +// CHECK: [[CALL:%.*]] = call i8* @objc_getProperty( +// CHECK-NEXT: ret i8* [[CALL]] + +// rdar://problem/9315552 +void test46(__weak id *wp, __weak volatile id *wvp) { + extern id test46_helper(void); + + // TODO: this is sub-optimal, we should retain at the actual call site. + + // CHECK: [[T0:%.*]] = call i8* @test46_helper() + // CHECK-NEXT: [[T1:%.*]] = load i8*** {{%.*}}, align 8 + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_storeWeak(i8** [[T1]], i8* [[T0]]) + // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]]) + // CHECK-NEXT: store i8* [[T3]], i8** + id x = *wp = test46_helper(); + + // CHECK: [[T0:%.*]] = call i8* @test46_helper() + // CHECK-NEXT: [[T1:%.*]] = load i8*** {{%.*}}, align 8 + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_storeWeak(i8** [[T1]], i8* [[T0]]) + // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]]) + // CHECK-NEXT: store i8* [[T3]], i8** + id y = *wvp = test46_helper(); +} + +// rdar://problem/9378887 +void test47(void) { + extern id test47_helper(void); + id x = x = test47_helper(); + + // CHECK: define void @test47() + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: store i8* null, i8** [[X]] + // CHECK-NEXT: [[CALL:%.*]] = call i8* @test47_helper() + // CHECK-NEXT: [[T0:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[CALL]]) + // CHECK-NEXT: [[T1:%.*]] = load i8** [[X]] + // CHECK-NEXT: store i8* [[T0]], i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T0]]) + // CHECK-NEXT: [[T3:%.*]] = load i8** [[X]] + // CHECK-NEXT: store i8* [[T2]], i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T3]]) + // CHECK-NEXT: [[T4:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T4]]) + // CHECK-NEXT: ret void +} + +void test48(void) { + extern id test48_helper(void); + __weak id x = x = test48_helper(); + // CHECK: define void @test48() + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: [[T0:%.*]] = call i8* @objc_initWeak(i8** [[X]], i8* null) + // CHECK-NEXT: [[T1:%.*]] = call i8* @test48_helper() + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_storeWeak(i8** [[X]], i8* [[T1]]) + // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_storeWeak(i8** [[X]], i8* [[T2]]) + // CHECK-NEXT: call void @objc_destroyWeak(i8** [[X]]) + // CHECK-NEXT: ret void +} + +void test49(void) { + extern id test49_helper(void); + __autoreleasing id x = x = test49_helper(); + // CHECK: define void @test49() + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: store i8* null, i8** [[X]] + // CHECK-NEXT: [[CALL:%.*]] = call i8* @test49_helper() + // CHECK-NEXT: [[T0:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[CALL]]) + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_autorelease(i8* [[T0]]) + // CHECK-NEXT: store i8* [[T2]], i8** [[X]] + // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retainAutorelease(i8* [[T1]]) + // CHECK-NEXT: store i8* [[T3]], i8** [[X]] + // CHECK-NEXT: ret void +} + +// rdar://9380136 +id x(); +void test50(id y) { + ({x();}); +// CHECK: [[T0:%.*]] = call i8* @objc_retain +// CHECK: call void @objc_release +} + + +// rdar://9400762 +struct CGPoint { + float x; + float y; +}; +typedef struct CGPoint CGPoint; + +@interface Foo +@property (assign) CGPoint point; +@end + +@implementation Foo +@synthesize point; +@end + +// rdar://problem/9400398 +id test52(void) { + id test52_helper(int) __attribute__((ns_returns_retained)); + return ({ int x = 5; test52_helper(x); }); + +// CHECK: define i8* @test52() +// CHECK: [[X:%.*]] = alloca i32 +// CHECK-NEXT: store i32 5, i32* [[X]], +// CHECK-NEXT: [[T0:%.*]] = load i32* [[X]], +// CHECK-NEXT: [[T1:%.*]] = call i8* @test52_helper(i32 [[T0]]) +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_autoreleaseReturnValue(i8* [[T1]]) +// CHECK-NEXT: ret i8* [[T2]] +} + +// rdar://problem/9400644 +void test53(void) { + id test53_helper(void); + id x = ({ id y = test53_helper(); y; }); + (void) x; +// CHECK: define void @test53() +// CHECK: [[X:%.*]] = alloca i8*, +// CHECK-NEXT: [[Y:%.*]] = alloca i8*, +// CHECK-NEXT: [[T0:%.*]] = call i8* @test53_helper() +// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) +// CHECK-NEXT: store i8* [[T1]], i8** [[Y]], +// CHECK-NEXT: [[T0:%.*]] = load i8** [[Y]], +// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) +// CHECK-NEXT: [[T2:%.*]] = load i8** [[Y]] +// CHECK-NEXT: call void @objc_release(i8* [[T2]]) +// CHECK-NEXT: store i8* [[T1]], i8** [[X]], +// CHECK-NEXT: load i8** [[X]], +// CHECK-NEXT: [[T0:%.*]] = load i8** [[X]] +// CHECK-NEXT: call void @objc_release(i8* [[T0]]) +// CHECK-NEXT: ret void +} diff --git a/test/CodeGenObjC/autorelease.m b/test/CodeGenObjC/autorelease.m new file mode 100644 index 0000000000..1d81d010a3 --- /dev/null +++ b/test/CodeGenObjC/autorelease.m @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -fobjc-nonfragile-abi -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -emit-llvm -fobjc-nonfragile-abi -o - %s | FileCheck %s +// rdar://8881826 +// rdar://9412038 + +@interface I +{ + id ivar; +} +- (id) Meth; ++ (id) MyAlloc;; +@end + +@implementation I +- (id) Meth { + @autoreleasepool { + id p = [I MyAlloc]; + if (!p) + return ivar; + } + return 0; +} ++ (id) MyAlloc { + return 0; +} +@end + +// CHECK: call i8* @objc_autoreleasePoolPush +// CHECK: [[T:%.*]] = load i8** [[A:%.*]] +// CHECK: call void @objc_autoreleasePoolPop diff --git a/test/CodeGenObjC/block-6.m b/test/CodeGenObjC/block-6.m index 3720a87b9e..44c7a78e69 100644 --- a/test/CodeGenObjC/block-6.m +++ b/test/CodeGenObjC/block-6.m @@ -2,10 +2,16 @@ // rdar://8893785 void MYFUNC() { -// CHECK: [[T1:%.*]] = bitcast i8* ()* -// CHECK-NEXT: [[FORWARDING:%.*]] = getelementptr inbounds [[N_T:%.*]]* [[N:%.*]], i32 0, i32 1 -// CHECK-NEXT: [[T0:%.*]] = load [[N_T]]** [[FORWARDING]] -// CHECK-NEXT: [[OBSERVER:%.*]] = getelementptr inbounds [[N_T]]* [[T0]], i32 0, i32 6 +// CHECK: define void @MYFUNC() +// CHECK: [[OBSERVER_SLOT:%.*]] = alloca [[OBSERVER_T:%.*]], align 8 + +// CHECK: [[T0:%.*]] = getelementptr inbounds [[OBSERVER_T]]* [[OBSERVER_SLOT]], i32 0, i32 1 +// CHECK: store [[OBSERVER_T]]* [[OBSERVER_SLOT]], [[OBSERVER_T]]** [[T0]] + +// CHECK: [[T1:%.*]] = bitcast i8* ()* +// CHECK: [[FORWARDING:%.*]] = getelementptr inbounds [[OBSERVER_T]]* [[OBSERVER_SLOT]], i32 0, i32 1 +// CHECK-NEXT: [[T0:%.*]] = load [[OBSERVER_T]]** [[FORWARDING]] +// CHECK-NEXT: [[OBSERVER:%.*]] = getelementptr inbounds [[OBSERVER_T]]* [[T0]], i32 0, i32 6 // CHECK-NEXT: store i8* [[T1]], i8** [[OBSERVER]] __block id observer = ^{ return observer; }; } diff --git a/test/CodeGenObjC/mrr-autorelease.m b/test/CodeGenObjC/mrr-autorelease.m new file mode 100644 index 0000000000..10f549fcec --- /dev/null +++ b/test/CodeGenObjC/mrr-autorelease.m @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple i386-apple-darwin10 -emit-llvm -o - %s | FileCheck %s +// rdar://8881826 +// rdar://9423507 + +@interface I +{ + id ivar; +} +- (id) Meth; +@end + +@implementation I +- (id) Meth { + @autoreleasepool { + } + return 0; +} +@end + +// CHECK-NOT: call i8* @objc_getClass +// CHECK: call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend +// CHECK: call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend +// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend diff --git a/test/CodeGenObjC/nonlazy-msgSend.m b/test/CodeGenObjC/nonlazy-msgSend.m new file mode 100644 index 0000000000..3d7ba10dc5 --- /dev/null +++ b/test/CodeGenObjC/nonlazy-msgSend.m @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -emit-llvm -o %t %s +// RUN: grep -F 'declare i8* @objc_msgSend(i8*, i8*, ...) nonlazybind' %t + +void f0(id x) { + [x foo]; +} diff --git a/test/CodeGenObjC/related-result-type.m b/test/CodeGenObjC/related-result-type.m new file mode 100644 index 0000000000..ef38661f94 --- /dev/null +++ b/test/CodeGenObjC/related-result-type.m @@ -0,0 +1,54 @@ +// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s + +@interface NSObject ++ (id)alloc; +- (id)init; +- (id)retain; +@end + +@interface NSString : NSObject +@end + +// CHECK: define void @test1() +void test1() { + // CHECK: {{call.*@objc_msgSend}} + // CHECK: {{call.*@objc_msgSend}} + // CHECK: {{call.*@objc_msgSend}} + // CHECK: bitcast i8* + NSString *str1 = [[[NSString alloc] init] retain]; +} + +// CHECK: define void @test2() +void test2() { + // CHECK: {{call.*@objc_msgSend}} + // CHECK: {{call.*@objc_msgSend}} + // CHECK: {{call.*@objc_msgSend}} + // CHECK: bitcast i8* + NSString *str1 = NSString.alloc.init.retain; +} + +@interface Test2 : NSString +- (id)init; +@end + +@implementation Test2 +// CHECK: define internal i8* @"\01-[Test2 init]" +- (id)init { + // CHECK: {{call.*@objc_msgSendSuper}} + // CHECK-NEXT: bitcast i8* + return [super init]; +} +@end + +@interface Test3 : NSString +- (id)init; +@end + +@implementation Test3 +// CHECK: define internal i8* @"\01-[Test3 init]" +- (id)init { + // CHECK: {{call.*@objc_msgSendSuper}} + // CHECK-NEXT: bitcast i8* + return [super init]; +} +@end diff --git a/test/CodeGenObjCXX/arc-globals.mm b/test/CodeGenObjCXX/arc-globals.mm new file mode 100644 index 0000000000..7167dbc366 --- /dev/null +++ b/test/CodeGenObjCXX/arc-globals.mm @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fblocks -fobjc-arc -O2 -disable-llvm-optzns -o - %s | FileCheck %s + +// Test that we're properly retaining lifetime-qualified pointers +// initialized statically and wrapping up those initialization in an +// autorelease pool. +id getObject(); + +// CHECK: define internal void @__cxx_global_var_init +// CHECK: call i8* @_Z9getObjectv +// CHECK-NEXT: call i8* @objc_retainAutoreleasedReturnValue +// CHECK-NEXT: {{store i8*.*@global_obj}} +// CHECK-NEXT: ret void +id global_obj = getObject(); + +// CHECK: define internal void @__cxx_global_var_init +// CHECK: call i8* @_Z9getObjectv +// CHECK-NEXT: call i8* @objc_retainAutoreleasedReturnValue +// CHECK-NEXT: {{store i8*.*@global_obj2}} +// CHECK-NEXT: ret void +id global_obj2 = getObject(); + +// CHECK: define internal void @_GLOBAL__I_a +// CHECK: call i8* @objc_autoreleasePoolPush() +// CHECK-NEXT: call void @__cxx_global_var_init +// CHECK-NEXT: call void @__cxx_global_var_init1 +// CHECK-NEXT: call void @objc_autoreleasePoolPop( +// CHECK-NEXT: ret void diff --git a/test/CodeGenObjCXX/arc-mangle.mm b/test/CodeGenObjCXX/arc-mangle.mm new file mode 100644 index 0000000000..628144f04c --- /dev/null +++ b/test/CodeGenObjCXX/arc-mangle.mm @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fobjc-arc -emit-llvm -o - %s | FileCheck %s + +// CHECK: define void @_Z1fPU8__strongP11objc_object(i8**) +void f(__strong id *) {} +// CHECK: define void @_Z1fPU6__weakP11objc_object(i8**) +void f(__weak id *) {} +// CHECK: define void @_Z1fPU15__autoreleasingP11objc_object(i8**) +void f(__autoreleasing id *) {} +// CHECK: define void @_Z1fPU19__unsafe_unretainedP11objc_object(i8**) +void f(__unsafe_unretained id *) {} +// CHECK: define void @_Z1fPKU8__strongP11objc_object(i8**) +void f(const __strong id *) {} +// CHECK: define void @_Z1fPKU6__weakP11objc_object(i8**) +void f(const __weak id *) {} +// CHECK: define void @_Z1fPKU15__autoreleasingP11objc_object(i8**) +void f(const __autoreleasing id *) {} +// CHECK: define void @_Z1fPKU19__unsafe_unretainedP11objc_object(i8**) +void f(const __unsafe_unretained id *) {} + + +template struct unsigned_c { }; + +// CHECK: define weak_odr void @_Z1gIKvEvP10unsigned_cIXplszv1U8__bridgecvPT_v1U8__bridgecvP11objc_objectcvS3_Li0ELi1EEE +templatevoid g(unsigned_c*) {} +template void g(unsigned_c *); diff --git a/test/CodeGenObjCXX/arc-new-delete.mm b/test/CodeGenObjCXX/arc-new-delete.mm new file mode 100644 index 0000000000..e638870fdd --- /dev/null +++ b/test/CodeGenObjCXX/arc-new-delete.mm @@ -0,0 +1,86 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fobjc-arc -fblocks -triple x86_64-apple-darwin10.0.0 -emit-llvm -o - %s | FileCheck %s + +typedef __strong id strong_id; +typedef __weak id weak_id; + +// CHECK: define void @_Z8test_newP11objc_object +void test_new(id invalue) { + // CHECK: alloca i8* + // CHECK-NEXT: call i8* @objc_retain + + // CHECK: call noalias i8* @_Znwm + // CHECK-NEXT: {{bitcast i8\*.*to i8\*\*}} + // CHECK-NEXT: store i8* null, i8** + new strong_id; + // CHECK: call noalias i8* @_Znwm + // CHECK-NEXT: {{bitcast i8\*.*to i8\*\*}} + // CHECK-NEXT: store i8* null, i8** + new weak_id; + + // CHECK: call noalias i8* @_Znwm + // CHECK-NEXT: {{bitcast i8\*.*to i8\*\*}} + // CHECK-NEXT: store i8* null, i8** + new __strong id; + // CHECK: call noalias i8* @_Znwm + // CHECK-NEXT: {{bitcast i8\*.*to i8\*\*}} + // CHECK-NEXT: store i8* null, i8** + new __weak id; + + // CHECK: call noalias i8* @_Znwm + // CHECK: call i8* @objc_retain + // CHECK: store i8* + new __strong id(invalue); + + // CHECK: call noalias i8* @_Znwm + // CHECK: call i8* @objc_initWeak + new __weak id(invalue); + + // CHECK: call void @objc_release + // CHECK: ret void +} + +// CHECK: define void @_Z14test_array_new +void test_array_new() { + // CHECK: call noalias i8* @_Znam + // CHECK: store i64 17, i64* + // CHECK: call void @llvm.memset.p0i8.i64 + new strong_id[17]; + + // CHECK: call noalias i8* @_Znam + // CHECK: store i64 17, i64* + // CHECK: call void @llvm.memset.p0i8.i64 + new weak_id[17]; + // CHECK: ret void +} + +// CHECK: define void @_Z11test_deletePU8__strongP11objc_objectPU6__weakS0_ +void test_delete(__strong id *sptr, __weak id *wptr) { + // CHECK: br i1 + // CHECK: load i8** + // CHECK-NEXT: call void @objc_release + // CHECK: call void @_ZdlPv + delete sptr; + + // CHECK: call void @objc_destroyWeak + // CHECK: call void @_ZdlPv + delete wptr; + + // CHECK: ret void +} + +// CHECK: define void @_Z17test_array_deletePU8__strongP11objc_objectPU6__weakS0_ +void test_array_delete(__strong id *sptr, __weak id *wptr) { + // CHECK: load i64* + // CHECK: {{icmp ne i64.*, 0}} + // CHECK: call void @objc_release + // CHECK: br label + // CHECK: call void @_ZdaPv + delete [] sptr; + + // CHECK: load i64* + // CHECK: {{icmp ne i64.*, 0}} + // CHECK: call void @objc_destroyWeak + // CHECK: br label + // CHECK: call void @_ZdaPv + delete [] wptr; +} diff --git a/test/CodeGenObjCXX/arc-pseudo-destructors.mm b/test/CodeGenObjCXX/arc-pseudo-destructors.mm new file mode 100644 index 0000000000..27d98cb7cb --- /dev/null +++ b/test/CodeGenObjCXX/arc-pseudo-destructors.mm @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fobjc-arc -fblocks -triple x86_64-apple-darwin10.0.0 -emit-llvm -o - %s | FileCheck %s + +// CHECK: define void @_Z28test_objc_object_pseudo_dtorPU8__strongP11objc_objectPU6__weakS0_ +void test_objc_object_pseudo_dtor(__strong id *ptr, __weak id *wptr) { + // CHECK: load i8*** + // CHECK-NEXT: load i8** + // CHECK-NEXT: call void @objc_release + ptr->~id(); + + // CHECK: call void @objc_destroyWeak(i8** {{%.*}}) + wptr->~id(); + + // CHECK: load i8*** + // CHECK-NEXT: load i8** + // CHECK-NEXT: call void @objc_release + (*ptr).~id(); + + // CHECK: call void @objc_destroyWeak(i8** {{%.*}}) + (*wptr).~id(); + // CHECK: ret void +} diff --git a/test/CodeGenObjCXX/arc-references.mm b/test/CodeGenObjCXX/arc-references.mm new file mode 100644 index 0000000000..de79176b7e --- /dev/null +++ b/test/CodeGenObjCXX/arc-references.mm @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fblocks -fobjc-arc -O2 -disable-llvm-optzns -o - %s | FileCheck %s + +id getObject(); +void callee(); + +// Lifetime extension for binding a reference to an rvalue +// CHECK: define void @_Z5test0v() +void test0() { + // CHECK: call i8* @_Z9getObjectv + // CHECK-NEXT:: call i8* @objc_retainAutoreleasedReturnValue + const __strong id &ref1 = getObject(); + // CHECK: call void @_Z6calleev + callee(); + // CHECK: call i8* @_Z9getObjectv + // CHECK-NEXT: call i8* @objc_retainAutoreleasedReturnValue + // CHECK-NEXT: call i8* @objc_autorelease + const __autoreleasing id &ref2 = getObject(); + // CHECK: call void @_Z6calleev + callee(); + // CHECK: call void @objc_release + // CHECK-NEXT: ret +} + +// No lifetime extension when we're binding a reference to an lvalue. +// CHECK: define void @_Z5test1RU8__strongP11objc_objectRU6__weakS0_ +void test1(__strong id &x, __weak id &y) { + // CHECK-NOT: release + const __strong id &ref1 = x; + const __autoreleasing id &ref2 = x; + const __weak id &ref3 = y; + // CHECK: ret void +} + +typedef __strong id strong_id; + +//CHECK: define void @_Z5test3v +void test3() { + // CHECK: call i8* @objc_initWeak + // CHECK-NEXT: store i8** + const __weak id &ref = strong_id(); + // CHECK-NEXT: call void @_Z6calleev() + callee(); + // CHECK-NEXT: call void @objc_destroyWeak + // CHECK-NEXT: ret void +} + +// CHECK: define internal void @__cxx_global_var_init( +// CHECK: call i8* @_Z9getObjectv +// CHECK-NEXT: call i8* @objc_retainAutoreleasedReturnValue +const __strong id &global_ref = getObject(); + +// Note: we intentionally don't release the object. + diff --git a/test/CodeGenObjCXX/arc-special-member-functions.mm b/test/CodeGenObjCXX/arc-special-member-functions.mm new file mode 100644 index 0000000000..598dd40610 --- /dev/null +++ b/test/CodeGenObjCXX/arc-special-member-functions.mm @@ -0,0 +1,154 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fobjc-arc -fblocks -triple x86_64-apple-darwin10.0.0 -emit-llvm -o - %s | FileCheck %s + +struct ObjCMember { + id member; +}; + +struct ObjCArrayMember { + id member[2][3]; +}; + +struct ObjCBlockMember { + int (^bp)(int); +}; + +// CHECK: define void @_Z42test_ObjCMember_default_construct_destructv( +void test_ObjCMember_default_construct_destruct() { + // CHECK: call void @_ZN10ObjCMemberC1Ev + // CHECK: call void @_ZN10ObjCMemberD1Ev + ObjCMember m1; +} + +// CHECK: define void @_Z39test_ObjCMember_copy_construct_destruct10ObjCMember +void test_ObjCMember_copy_construct_destruct(ObjCMember m1) { + // CHECK: call void @_ZN10ObjCMemberC1ERKS_ + // CHECK: call void @_ZN10ObjCMemberD1Ev + ObjCMember m2 = m1; + // CHECK: ret void +} + +// CHECK: define void @_Z27test_ObjCMember_copy_assign10ObjCMemberS_ +void test_ObjCMember_copy_assign(ObjCMember m1, ObjCMember m2) { + // CHECK: {{call.*_ZN10ObjCMemberaSERKS_}} + m1 = m2; + // CHECK-NEXT: ret void +} + +// Implicitly-generated copy assignment operator for ObjCMember +// CHECK: {{define linkonce_odr.*@_ZN10ObjCMemberaSERKS_}} +// CHECK: call void @objc_storeStrong +// CHECK: ret + +// CHECK: define void @_Z47test_ObjCArrayMember_default_construct_destructv +void test_ObjCArrayMember_default_construct_destruct() { + // CHECK: call void @_ZN15ObjCArrayMemberC1Ev + ObjCArrayMember m1; + // CHECK: call void @_ZN15ObjCArrayMemberD1Ev + // CHECK: ret void +} + +// CHECK: define void @_Z44test_ObjCArrayMember_copy_construct_destruct15ObjCArrayMember +void test_ObjCArrayMember_copy_construct_destruct(ObjCArrayMember m1) { + // CHECK: call void @_ZN15ObjCArrayMemberC1ERKS_ + ObjCArrayMember m2 = m1; + // CHECK: call void @_ZN15ObjCArrayMemberD1Ev + // CHECK: ret void +} + +void test_ObjCArrayMember_copy_assign(ObjCArrayMember m1, ObjCArrayMember m2) { + // CHECK: {{call.*@_ZN15ObjCArrayMemberaSERKS_}} + m1 = m2; + // CHECK-NEXT: ret void +} + +// Implicitly-generated copy assignment operator for ObjCArrayMember +// CHECK: {{define linkonce_odr.*@_ZN15ObjCArrayMemberaSERKS_}} +// CHECK: call void @objc_storeStrong +// CHECK-NEXT: br label +// CHECK: ret + +// CHECK: define void @_Z47test_ObjCBlockMember_default_construct_destructv +void test_ObjCBlockMember_default_construct_destruct() { + // CHECK: call void @_ZN15ObjCBlockMemberC1Ev + ObjCBlockMember m; + // CHECK-NEXT: call void @_ZN15ObjCBlockMemberD1Ev + // CHECK-NEXT: ret void +} + +// CHECK: define void @_Z44test_ObjCBlockMember_copy_construct_destruct15ObjCBlockMember +void test_ObjCBlockMember_copy_construct_destruct(ObjCBlockMember m1) { + // CHECK: call void @_ZN15ObjCBlockMemberC1ERKS_ + ObjCBlockMember m2 = m1; + // CHECK-NEXT: call void @_ZN15ObjCBlockMemberD1Ev + // CHECK-NEXT: ret void +} + +// CHECK: define void @_Z32test_ObjCBlockMember_copy_assign15ObjCBlockMemberS_ +void test_ObjCBlockMember_copy_assign(ObjCBlockMember m1, ObjCBlockMember m2) { + // CHECK: {{call.*_ZN15ObjCBlockMemberaSERKS_}} + m1 = m2; + // CHECK-NEXT: ret void +} + +// Implicitly-generated copy assignment operator for ObjCBlockMember +// CHECK: define linkonce_odr {{%.*}}* @_ZN15ObjCBlockMemberaSERKS_( +// CHECK: [[T0:%.*]] = call i8* @objc_retainBlock( +// CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to i32 (i32)* +// CHECK-NEXT: [[T2:%.*]] = load i32 (i32)** [[SLOT:%.*]], +// CHECK-NEXT: store i32 (i32)* [[T1]], i32 (i32)** [[SLOT]] +// CHECK-NEXT: [[T3:%.*]] = bitcast i32 (i32)* [[T2]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T3]]) +// CHECK-NEXT: ret + +// Implicitly-generated copy constructor for ObjCBlockMember +// CHECK: define linkonce_odr void @_ZN15ObjCBlockMemberC2ERKS_ +// CHECK: call i8* @objc_retainBlock +// CHECK: ret + +// Implicitly-generated destructor for ObjCBlockMember +// CHECK: define linkonce_odr void @_ZN15ObjCBlockMemberD2Ev +// CHECK: call void @objc_release(i8* +// CHECK: ret + +// Implicitly-generated default constructor for ObjCBlockMember +// CHECK: define linkonce_odr void @_ZN15ObjCBlockMemberC2Ev +// CHECK: store i32 (i32)* null, +// CHECK-NEXT: ret void + +// Implicitly-generated copy constructor for ObjCArrayMember +// CHECK: define linkonce_odr void @_ZN15ObjCArrayMemberC2ERKS_ +// CHECK: br i1 +// CHECK: call i8* @objc_retain +// CHECK-NEXT: store i8* +// CHECK-NEXT: br label +// CHECK: ret + +// Implicitly-generated destructor for ObjCArrayMember +// CHECK: define linkonce_odr void @_ZN15ObjCArrayMemberD2Ev +// CHECK: call void @objc_release +// CHECK: br label +// CHECK: ret + +// Implicitly-generated default constructor for ObjCArrayMember +// CHECK: define linkonce_odr void @_ZN15ObjCArrayMemberC2Ev +// CHECK: call void @llvm.memset.p0i8.i64 +// CHECK: ret + +// Implicitly-generated copy constructor for ObjCMember +// CHECK: define linkonce_odr void @_ZN10ObjCMemberC2ERKS_ +// CHECK-NOT: objc_release +// CHECK: call i8* @objc_retain +// CHECK-NEXT: store i8* +// CHECK-NEXT: ret void + +// Implicitly-generated destructor for ObjCMember +// CHECK: define linkonce_odr void @_ZN10ObjCMemberD2Ev +// CHECK: call void @objc_release +// CHECK: ret void + +// Implicitly-generated default constructor for ObjCMember +// CHECK: define linkonce_odr void @_ZN10ObjCMemberC2Ev +// CHECK-NOT: objc_release +// CHECK: store i8* null +// CHECK-NEXT: ret void + diff --git a/test/CodeGenObjCXX/arc.mm b/test/CodeGenObjCXX/arc.mm new file mode 100644 index 0000000000..5d46a818cf --- /dev/null +++ b/test/CodeGenObjCXX/arc.mm @@ -0,0 +1,152 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fblocks -fobjc-arc -O2 -disable-llvm-optzns -o - %s | FileCheck %s + +// rdar://problem/9315552 +// The analogous ObjC testcase test46 in arr.m. +void test0(__weak id *wp, __weak volatile id *wvp) { + extern id test0_helper(void); + + // TODO: this is sub-optimal, we should retain at the actual call site. + // TODO: in the non-volatile case, we do not need to be reloading. + + // CHECK: [[T0:%.*]] = call i8* @_Z12test0_helperv() + // CHECK-NEXT: [[T1:%.*]] = load i8*** {{%.*}}, align 8 + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_storeWeak(i8** [[T1]], i8* [[T0]]) + // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]]) + // CHECK-NEXT: store i8* [[T3]], i8** + id x = *wp = test0_helper(); + + // CHECK: [[T0:%.*]] = call i8* @_Z12test0_helperv() + // CHECK-NEXT: [[T1:%.*]] = load i8*** {{%.*}}, align 8 + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_storeWeak(i8** [[T1]], i8* [[T0]]) + // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_loadWeakRetained(i8** [[T1]]) + // CHECK-NEXT: store i8* [[T3]], i8** + id y = *wvp = test0_helper(); +} + +// rdar://problem/9320648 +struct Test1_helper { Test1_helper(); }; +@interface Test1 @end +@implementation Test1 { Test1_helper x; } @end +// CHECK: define internal i8* @"\01-[Test1 .cxx_construct]"( +// CHECK: call void @_ZN12Test1_helperC1Ev( +// CHECK-NEXT: load +// CHECK-NEXT: bitcast +// CHECK-NEXT: ret i8* + +void test34(int cond) { + __strong id strong; + __weak id weak; + extern void test34_sink(id *); + test34_sink(cond ? &strong : 0); + test34_sink(cond ? &weak : 0); + + // CHECK: define void @_Z6test34i( + // CHECK: [[COND:%.*]] = alloca i32 + // CHECK-NEXT: [[STRONG:%.*]] = alloca i8* + // CHECK-NEXT: [[WEAK:%.*]] = alloca i8* + // CHECK-NEXT: [[TEMP1:%.*]] = alloca i8* + // CHECK-NEXT: [[TEMP2:%.*]] = alloca i8* + // CHECK-NEXT: store i32 + // CHECK-NEXT: store i8* null, i8** [[STRONG]] + // CHECK-NEXT: call i8* @objc_initWeak(i8** [[WEAK]], i8* null) + + // CHECK-NEXT: [[T0:%.*]] = load i32* [[COND]] + // CHECK-NEXT: [[T1:%.*]] = icmp ne i32 [[T0]], 0 + // CHECK: [[ARG:%.*]] = phi i8** + // CHECK-NEXT: [[T0:%.*]] = icmp eq i8** [[ARG]], null + // CHECK-NEXT: [[T1:%.*]] = select i1 [[T0]], i8** null, i8** [[TEMP1]] + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[T0:%.*]] = load i8** [[ARG]] + // CHECK-NEXT: store i8* [[T0]], i8** [[TEMP1]] + // CHECK-NEXT: br label + // CHECK: call void @_Z11test34_sinkPU15__autoreleasingP11objc_object(i8** [[T1]]) + // CHECK-NEXT: [[T0:%.*]] = icmp eq i8** [[ARG]], null + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[T0:%.*]] = load i8** [[TEMP1]] + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = load i8** [[ARG]] + // CHECK-NEXT: store i8* [[T1]], i8** [[ARG]] + // CHECK-NEXT: call void @objc_release(i8* [[T2]]) + // CHECK-NEXT: br label + + // CHECK: [[T0:%.*]] = load i32* [[COND]] + // CHECK-NEXT: [[T1:%.*]] = icmp ne i32 [[T0]], 0 + // CHECK: [[ARG:%.*]] = phi i8** + // CHECK-NEXT: [[T0:%.*]] = icmp eq i8** [[ARG]], null + // CHECK-NEXT: [[T1:%.*]] = select i1 [[T0]], i8** null, i8** [[TEMP2]] + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[T0:%.*]] = call i8* @objc_loadWeak(i8** [[ARG]]) + // CHECK-NEXT: store i8* [[T0]], i8** [[TEMP2]] + // CHECK-NEXT: br label + // CHECK: call void @_Z11test34_sinkPU15__autoreleasingP11objc_object(i8** [[T1]]) + // CHECK-NEXT: [[T0:%.*]] = icmp eq i8** [[ARG]], null + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[T0:%.*]] = load i8** [[TEMP2]] + // CHECK-NEXT: call i8* @objc_storeWeak(i8** [[ARG]], i8* [[T0]]) + // CHECK-NEXT: br label + + // CHECK: call void @objc_destroyWeak(i8** [[WEAK]]) + // CHECK: ret void +} + +struct Test35_Helper { + static id makeObject1() __attribute__((ns_returns_retained)); + id makeObject2() __attribute__((ns_returns_retained)); + static id makeObject3(); + id makeObject4(); +}; + +// CHECK: define void @_Z6test3513Test35_HelperPS_ +void test35(Test35_Helper x0, Test35_Helper *x0p) { + // CHECK: call i8* @_ZN13Test35_Helper11makeObject1Ev + // CHECK-NOT: call i8* @objc_retain + id obj1 = Test35_Helper::makeObject1(); + // CHECK: call i8* @_ZN13Test35_Helper11makeObject2Ev + // CHECK-NOT: call i8* @objc_retain + id obj2 = x0.makeObject2(); + // CHECK: call i8* @_ZN13Test35_Helper11makeObject2Ev + // CHECK-NOT: call i8* @objc_retain + id obj3 = x0p->makeObject2(); + id (Test35_Helper::*pmf)() __attribute__((ns_returns_retained)) + = &Test35_Helper::makeObject2; + // CHECK: call i8* % + // CHECK-NOT: call i8* @objc_retain + id obj4 = (x0.*pmf)(); + // CHECK: call i8* % + // CHECK-NOT: call i8* @objc_retain + id obj5 = (x0p->*pmf)(); + + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK-NEXT: ret void +} + +// CHECK: define void @_Z7test35b13Test35_HelperPS_ +void test35b(Test35_Helper x0, Test35_Helper *x0p) { + // CHECK: call i8* @_ZN13Test35_Helper11makeObject3Ev + // CHECK: call i8* @objc_retain + id obj1 = Test35_Helper::makeObject3(); + // CHECK: call i8* @_ZN13Test35_Helper11makeObject4Ev + // CHECK: call i8* @objc_retain + id obj2 = x0.makeObject4(); + // CHECK: call i8* @_ZN13Test35_Helper11makeObject4Ev + // CHECK: call i8* @objc_retain + id obj3 = x0p->makeObject4(); + id (Test35_Helper::*pmf)() = &Test35_Helper::makeObject4; + // CHECK: call i8* % + // CHECK: call i8* @objc_retain + id obj4 = (x0.*pmf)(); + // CHECK: call i8* % + // CHECK: call i8* @objc_retain + id obj5 = (x0p->*pmf)(); + + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK-NEXT: ret void +} diff --git a/test/CodeGenObjCXX/copy.mm b/test/CodeGenObjCXX/copy.mm new file mode 100644 index 0000000000..133910f25d --- /dev/null +++ b/test/CodeGenObjCXX/copy.mm @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s + +// rdar://problem/9158302 +// This should not use a memmove_collectable in non-GC mode. +namespace test0 { + struct A { + id x; + }; + + // CHECK: define [[A:%.*]]* @_ZN5test04testENS_1AE( + // CHECK: alloca + // CHECK-NEXT: getelementptr + // CHECK-NEXT: store + // CHECK-NEXT: call noalias i8* @_Znwm( + // CHECK-NEXT: bitcast + // CHECK-NEXT: bitcast + // CHECK-NEXT: call void @llvm.memset.p0i8.i64( + // CHECK-NEXT: bitcast + // CHECK-NEXT: bitcast + // CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64( + // CHECK-NEXT: ret + A *test(A a) { + return new A(a); + } +} + diff --git a/test/Driver/arc-exceptions.m b/test/Driver/arc-exceptions.m new file mode 100644 index 0000000000..4501ccd073 --- /dev/null +++ b/test/Driver/arc-exceptions.m @@ -0,0 +1,5 @@ +// RUN: %clang -### -x objective-c -arch x86_64 -fobjc-arc -fsyntax-only %s 2> %t.log +// RUN: grep objective-c %t.log +// RUN: not grep "fobjc-arc-exceptions" %t.log +// RUN: %clang -### -x objective-c++ -arch x86_64 -fobjc-arc -fsyntax-only %s 2> %t.log +// RUN: grep "fobjc-arc-exceptions" %t.log diff --git a/test/Driver/arc.c b/test/Driver/arc.c new file mode 100644 index 0000000000..a4d4ed1c60 --- /dev/null +++ b/test/Driver/arc.c @@ -0,0 +1,14 @@ +// RUN: %clang -ObjC -ccc-host-triple i386-apple-darwin9 -m32 -fobjc-arc %s -fsyntax-only 2>&1 | FileCheck %s +// RUN: %clang -x objective-c -ccc-host-triple i386-apple-darwin9 -m32 -fobjc-arc %s -fsyntax-only 2>&1 | FileCheck %s +// RUN: %clang -x objective-c++ -ccc-host-triple i386-apple-darwin9 -m32 -fobjc-arc %s -fsyntax-only 2>&1 | FileCheck %s +// RUN: %clang -x c -ccc-host-triple i386-apple-darwin9 -m32 -fobjc-arc %s -fsyntax-only 2>&1 | FileCheck -check-prefix NOTOBJC %s +// RUN: %clang -x c++ -ccc-host-triple i386-apple-darwin9 -m32 -fobjc-arc %s -fsyntax-only 2>&1 | FileCheck -check-prefix NOTOBJC %s + +// Just to test clang is working. +# foo + +// CHECK: error: -fobjc-arc is not supported with fragile abi +// CHECK-NOT: invalid preprocessing directive + +// NOTOBJC-NOT: error: -fobjc-arc is not supported with fragile abi +// NOTOBJC: invalid preprocessing directive diff --git a/test/Driver/ios-simulator-arcruntime.c b/test/Driver/ios-simulator-arcruntime.c new file mode 100644 index 0000000000..7ecb34363a --- /dev/null +++ b/test/Driver/ios-simulator-arcruntime.c @@ -0,0 +1,5 @@ +// RUN: %clang -### -arch i386 -mmacosx-version-min=10.6 -D__IPHONE_OS_VERSION_MIN_REQUIRED=40201 -fobjc-arc -fsyntax-only %s 2>&1 | FileCheck -check-prefix=CHECK-OPTIONS1 %s +// RUN: %clang -### -arch i386 -mmacosx-version-min=10.6 -D__IPHONE_OS_VERSION_MIN_REQUIRED=50000 -fobjc-arc -fsyntax-only %s 2>&1 | FileCheck -check-prefix=CHECK-OPTIONS2 %s + +// CHECK-OPTIONS1: -fobjc-no-arc-runtime +// CHECK-OPTIONS2-NOT: -fobjc-no-arc-runtime diff --git a/test/Driver/no-objc-arr.m b/test/Driver/no-objc-arr.m new file mode 100644 index 0000000000..15fbdc20ce --- /dev/null +++ b/test/Driver/no-objc-arr.m @@ -0,0 +1,8 @@ +// RUN: %clang -Werror -fobjc-arc -fsyntax-only -fno-objc-arc -verify %s +// rdar://8949617 + +void * FOO() { + id string = @"Hello World.\n"; + void *pointer = string; // No error must be issued + return pointer; +} diff --git a/test/Index/arc-annotate.m b/test/Index/arc-annotate.m new file mode 100644 index 0000000000..b836bc8b26 --- /dev/null +++ b/test/Index/arc-annotate.m @@ -0,0 +1,38 @@ +@interface A +@property (strong, nonatomic) id property; +@property (nonatomic, weak) id second_property; +@property (unsafe_unretained, nonatomic) id third_property; +@end + +// RUN: c-index-test -test-annotate-tokens=%s:1:1:5:1 %s -fobjc-arc -fobjc-nonfragile-abi | FileCheck %s +// CHECK: Punctuation: "@" [1:1 - 1:2] ObjCInterfaceDecl=A:1:12 +// CHECK: Keyword: "interface" [1:2 - 1:11] ObjCInterfaceDecl=A:1:12 +// CHECK: Identifier: "A" [1:12 - 1:13] ObjCInterfaceDecl=A:1:12 +// CHECK: Punctuation: "@" [2:1 - 2:2] ObjCPropertyDecl=property:2:34 +// CHECK: Keyword: "property" [2:2 - 2:10] ObjCPropertyDecl=property:2:34 +// CHECK: Punctuation: "(" [2:11 - 2:12] ObjCPropertyDecl=property:2:34 +// CHECK: Keyword: "strong" [2:12 - 2:18] ObjCPropertyDecl=property:2:34 +// CHECK: Punctuation: "," [2:18 - 2:19] ObjCPropertyDecl=property:2:34 +// CHECK: Keyword: "nonatomic" [2:20 - 2:29] ObjCPropertyDecl=property:2:34 +// CHECK: Punctuation: ")" [2:29 - 2:30] ObjCPropertyDecl=property:2:34 +// CHECK: Identifier: "id" [2:31 - 2:33] TypeRef=id:0:0 +// CHECK: Identifier: "property" [2:34 - 2:42] ObjCPropertyDecl=property:2:34 +// CHECK: Punctuation: ";" [2:42 - 2:43] ObjCInterfaceDecl=A:1:12 +// CHECK: Punctuation: "@" [3:1 - 3:2] ObjCPropertyDecl=second_property:3:32 +// CHECK: Keyword: "property" [3:2 - 3:10] ObjCPropertyDecl=second_property:3:32 +// CHECK: Punctuation: "(" [3:11 - 3:12] ObjCPropertyDecl=second_property:3:32 +// CHECK: Keyword: "nonatomic" [3:12 - 3:21] ObjCPropertyDecl=second_property:3:32 +// CHECK: Punctuation: "," [3:21 - 3:22] ObjCPropertyDecl=second_property:3:32 +// CHECK: Keyword: "weak" [3:23 - 3:27] ObjCPropertyDecl=second_property:3:32 +// CHECK: Punctuation: ")" [3:27 - 3:28] ObjCPropertyDecl=second_property:3:32 +// CHECK: Identifier: "id" [3:29 - 3:31] TypeRef=id:0:0 +// CHECK: Identifier: "second_property" [3:32 - 3:47] ObjCPropertyDecl=second_property:3:32 +// CHECK: Punctuation: "@" [4:1 - 4:2] ObjCPropertyDecl=third_property:4:45 +// CHECK: Keyword: "property" [4:2 - 4:10] ObjCPropertyDecl=third_property:4:45 +// CHECK: Punctuation: "(" [4:11 - 4:12] ObjCPropertyDecl=third_property:4:45 +// CHECK: Keyword: "unsafe_unretained" [4:12 - 4:29] ObjCPropertyDecl=third_property:4:45 +// CHECK: Punctuation: "," [4:29 - 4:30] ObjCPropertyDecl=third_property:4:45 +// CHECK: Keyword: "nonatomic" [4:31 - 4:40] ObjCPropertyDecl=third_property:4:45 +// CHECK: Punctuation: ")" [4:40 - 4:41] ObjCPropertyDecl=third_property:4:45 +// CHECK: Identifier: "id" [4:42 - 4:44] TypeRef=id:0:0 +// CHECK: Identifier: "third_property" [4:45 - 4:59] ObjCPropertyDecl=third_property:4:45 diff --git a/test/Index/arc-complete.m b/test/Index/arc-complete.m new file mode 100644 index 0000000000..328983c441 --- /dev/null +++ b/test/Index/arc-complete.m @@ -0,0 +1,16 @@ +typedef const void *CFTypeRef; + +void test(id x) { + (__bridge CFTypeRef)x; +} + + + +// RUN: c-index-test -code-completion-at=%s:4:4 %s -fobjc-arc -fobjc-nonfragile-abi | FileCheck -check-prefix=CHECK-CC1 %s +// CHECK-CC1: macro definition:{TypedText __autoreleasing} (70) +// CHECK-CC1: NotImplemented:{TypedText __bridge}{HorizontalSpace }{Placeholder type}{RightParen )}{Placeholder expression} (40) +// CHECK-CC1: NotImplemented:{TypedText __bridge_retained}{HorizontalSpace }{Placeholder CF type}{RightParen )}{Placeholder expression} (40) +// CHECK-CC1: NotImplemented:{TypedText __bridge_transfer}{HorizontalSpace }{Placeholder Objective-C type}{RightParen )}{Placeholder expression} (40) +// CHECK-CC1: macro definition:{TypedText __strong} (70) +// CHECK-CC1: macro definition:{TypedText __unsafe_unretained} (70) +// CHECK-CC1: macro definition:{TypedText __weak} (70) diff --git a/test/Index/complete-exprs.m b/test/Index/complete-exprs.m index 0446dcd2fb..dfa114dcc0 100644 --- a/test/Index/complete-exprs.m +++ b/test/Index/complete-exprs.m @@ -7,10 +7,10 @@ typedef signed char BOOL; @property int prop1; @end - +__strong id global; @implementation A - (int)method:(id)param1 { - + void foo(id (^block)(id x, A* y)); for(BOOL B = YES; ; ) { } } @end @@ -27,3 +27,8 @@ typedef signed char BOOL; // CHECK-CC2: TypedefDecl:{TypedText BOOL} (50) // CHECK-CC2: NotImplemented:{TypedText char} (50) // CHECK-CC2: NotImplemented:{TypedText sizeof}{LeftParen (}{Placeholder expression-or-type}{RightParen )} (40) +// RUN: c-index-test -code-completion-at=%s:15:1 -fobjc-arc -fobjc-nonfragile-abi %s | FileCheck -check-prefix=CHECK-CC3 %s +// RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_COMPLETION_CACHING=1 c-index-test -code-completion-at=%s:15:1 -fobjc-arc -fobjc-nonfragile-abi %s | FileCheck -check-prefix=CHECK-CC3 %s +// CHECK-CC3: FunctionDecl:{ResultType void}{TypedText foo}{LeftParen (}{Placeholder ^id(id x, A *y)block}{RightParen )} (34) +// CHECK-CC3: VarDecl:{ResultType id}{TypedText global} (50) +// CHECK-CC3: ParmDecl:{ResultType id}{TypedText param1} (34) diff --git a/test/Index/complete-property-flags.m b/test/Index/complete-property-flags.m index af9e214a29..f2e08c3660 100644 --- a/test/Index/complete-property-flags.m +++ b/test/Index/complete-property-flags.m @@ -16,6 +16,8 @@ // CHECK-CC1-NEXT: {TypedText readwrite} // CHECK-CC1-NEXT: {TypedText retain} // CHECK-CC1-NEXT: {TypedText setter}{Text = }{Placeholder method} +// CHECK-CC1-NEXT: {TypedText strong} +// CHECK-CC1-NEXT: {TypedText unsafe_unretained} // RUN: c-index-test -code-completion-at=%s:8:18 %s | FileCheck -check-prefix=CHECK-CC2 %s // CHECK-CC2: {TypedText getter}{Text = }{Placeholder method} // CHECK-CC2-NEXT: {TypedText nonatomic} diff --git a/test/Lexer/has_feature_objc_arc.m b/test/Lexer/has_feature_objc_arc.m new file mode 100644 index 0000000000..18dab422b6 --- /dev/null +++ b/test/Lexer/has_feature_objc_arc.m @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -E %s -fobjc-nonfragile-abi -fobjc-arc "-triple" "x86_64-apple-macosx10.7.0" | FileCheck --check-prefix=CHECK-ARC %s +// RUN: %clang_cc1 -E %s -fobjc-nonfragile-abi -fobjc-arc "-triple" "x86_64-apple-macosx10.6.0" -fobjc-no-arc-runtime | FileCheck --check-prefix=CHECK-ARCLITE %s + +#if __has_feature(objc_arc) +void has_objc_arc_feature(); +#else +void no_objc_arc_feature(); +#endif + +#if __has_feature(objc_arc_weak) +void has_objc_arc_weak_feature(); +#else +void no_objc_arc_weak_feature(); +#endif + +// CHECK-ARC: void has_objc_arc_feature(); +// CHECK-ARC: void has_objc_arc_weak_feature(); + +// CHECK-ARCLITE: void has_objc_arc_feature(); +// CHECK-ARCLITE: void no_objc_arc_weak_feature(); diff --git a/test/PCH/Inputs/arc.h b/test/PCH/Inputs/arc.h new file mode 100644 index 0000000000..793fc643f2 --- /dev/null +++ b/test/PCH/Inputs/arc.h @@ -0,0 +1,20 @@ +// Header for Objective-C ARC-related PCH tests + +typedef const void *CFTypeRef; +typedef const struct __CFString *CFStringRef; + +CFTypeRef CFCreateSomething(); +CFStringRef CFCreateString(); +CFTypeRef CFGetSomething(); +CFStringRef CFGetString(); + +@interface NSString +@end + +id CreateSomething(); +NSString *CreateNSString(); + +typedef int array0[sizeof((__bridge id)CFCreateSomething())]; +typedef int array1[sizeof((__bridge CFTypeRef)CreateSomething())]; + + diff --git a/test/PCH/arc.m b/test/PCH/arc.m new file mode 100644 index 0000000000..6f7b8704d9 --- /dev/null +++ b/test/PCH/arc.m @@ -0,0 +1,9 @@ +// Test this without pch. +// RUN: %clang_cc1 -fblocks -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fobjc-arc -include %S/Inputs/arc.h -fsyntax-only -emit-llvm -o - %s + +// Test with pch. +// RUN: %clang_cc1 -emit-pch -fblocks -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fobjc-arc -x objective-c-header -o %t %S/Inputs/arc.h +// RUN: %clang_cc1 -fblocks -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fobjc-arc -include-pch %t -fsyntax-only -emit-llvm -o - %s + +array0 a0; +array1 a1; diff --git a/test/SemaObjC/Inputs/arc-system-header.h b/test/SemaObjC/Inputs/arc-system-header.h new file mode 100644 index 0000000000..9e76cd84ab --- /dev/null +++ b/test/SemaObjC/Inputs/arc-system-header.h @@ -0,0 +1,42 @@ +static inline void *test0(id x) { + return x; +} + +static inline void **test1(__strong id* x) { + return (void**) x; +} + + + + + +struct Test3 { + id *field; +}; + +@interface Test4 { +@public + id *field1; + __strong id *field2; +} +@end + +struct Test5 { + id field; +}; + + + + + + + +extern struct Test6 *const kMagicConstant; + + + + + +@interface Test7 +@property id *prop; +@end diff --git a/test/SemaObjC/arc-bridged-cast.m b/test/SemaObjC/arc-bridged-cast.m new file mode 100644 index 0000000000..227cce2248 --- /dev/null +++ b/test/SemaObjC/arc-bridged-cast.m @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -fblocks -verify %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fblocks %s + +typedef const void *CFTypeRef; +typedef const struct __CFString *CFStringRef; + +@interface NSString +@end + +CFTypeRef CFCreateSomething(); +CFStringRef CFCreateString(); +CFTypeRef CFGetSomething(); +CFStringRef CFGetString(); + +id CreateSomething(); +NSString *CreateNSString(); + +void from_cf() { + id obj1 = (__bridge_transfer id)CFCreateSomething(); + id obj2 = (__bridge_transfer NSString*)CFCreateString(); + (__bridge int*)CFCreateSomething(); // expected-error{{incompatible types casting 'CFTypeRef' (aka 'const void *') to 'int *' with a __bridge cast}} + id obj3 = (__bridge id)CFGetSomething(); + id obj4 = (__bridge NSString*)CFGetString(); +} + +void to_cf(id obj) { + CFTypeRef cf1 = (__bridge_retained CFTypeRef)CreateSomething(); + CFStringRef cf2 = (__bridge_retained CFStringRef)CreateNSString(); + CFTypeRef cf3 = (__bridge CFTypeRef)CreateSomething(); + CFStringRef cf4 = (__bridge CFStringRef)CreateNSString(); +} + +void fixits() { + id obj1 = (id)CFCreateSomething(); // expected-error{{cast of C pointer type 'CFTypeRef' (aka 'const void *') to Objective-C pointer type 'id' requires a bridged cast}} \ + // expected-note{{use __bridge to convert directly (no change in ownership)}} \ + // expected-note{{use __bridge_transfer to transfer ownership of a +1 'CFTypeRef' (aka 'const void *') into ARC}} + CFTypeRef cf1 = (CFTypeRef)CreateSomething(); // expected-error{{cast of Objective-C pointer type 'id' to C pointer type 'CFTypeRef' (aka 'const void *') requires a bridged cast}} \ + // expected-note{{use __bridge to convert directly (no change in ownership)}} \ + // expected-note{{use __bridge_retained to make an ARC object available as a +1 'CFTypeRef' (aka 'const void *')}} +} diff --git a/test/SemaObjC/arc-decls.m b/test/SemaObjC/arc-decls.m new file mode 100644 index 0000000000..c7efde0794 --- /dev/null +++ b/test/SemaObjC/arc-decls.m @@ -0,0 +1,64 @@ +// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -fobjc-nonfragile-abi -verify %s + +// rdar://8843524 + +struct A { + id x; // expected-error {{ARC forbids Objective-C objects in structs or unions}} +}; + +union u { + id u; // expected-error {{ARC forbids Objective-C objects in structs or unions}} +}; + +@interface I { + struct A a; + struct B { + id y[10][20]; // expected-error {{ARC forbids Objective-C objects in structs or unions}} + id z; + } b; + + union u c; +}; +@end + +struct S { + id __attribute__((objc_lifetime(none))) i; + void * vp; + int i1; +}; + +// rdar://9046528 + +@class NSError; + +__autoreleasing id X; // expected-error {{global variables cannot have __autoreleasing lifetime}} +__autoreleasing NSError *E; // expected-error {{global variables cannot have __autoreleasing lifetime}} + + +extern id __autoreleasing X1; // expected-error {{global variables cannot have __autoreleasing lifetime}} + +void func() +{ + id X; + static id __autoreleasing X1; // expected-error {{global variables cannot have __autoreleasing lifetime}} + extern id __autoreleasing E; // expected-error {{global variables cannot have __autoreleasing lifetime}} + +} + +// rdar://9157348 + +@interface J +@property (retain) id newFoo; // expected-note {{property declared here}} +@property (strong) id copyBar; // expected-note {{property declared here}} +@property (copy) id allocBaz; // expected-note {{property declared here}} +@property (copy, nonatomic) id new; +@end + +@implementation J +@synthesize newFoo; // expected-error {{property's synthesized getter follows Cocoa naming convention for returning}} +@synthesize copyBar; // expected-error {{property's synthesized getter follows Cocoa naming convention for returning}} +@synthesize allocBaz; // expected-error {{property's synthesized getter follows Cocoa naming convention for returning}} +@synthesize new; +- new {return 0; }; +@end + diff --git a/test/SemaObjC/arc-no-runtime.m b/test/SemaObjC/arc-no-runtime.m new file mode 100644 index 0000000000..0bf7d52fd2 --- /dev/null +++ b/test/SemaObjC/arc-no-runtime.m @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fobjc-arc -fobjc-nonfragile-abi -fobjc-no-arc-runtime -verify %s + +// rdar://problem/9150784 +void test(void) { + __weak id x; // expected-error {{the current deployment target does not support automated __weak references}} +} + +@interface A +@property (weak) id testObjectWeakProperty; // expected-note {{declared here}} +@end + +@implementation A +// rdar://9605088 +@synthesize testObjectWeakProperty; // expected-error {{the current deployment target does not support automated __weak references}} +@end diff --git a/test/SemaObjC/arc-non-pod-memaccess.m b/test/SemaObjC/arc-non-pod-memaccess.m new file mode 100644 index 0000000000..62d07ae122 --- /dev/null +++ b/test/SemaObjC/arc-non-pod-memaccess.m @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify -fblocks -triple x86_64-apple-darwin10.0.0 %s +// RUN: %clang_cc1 -x objective-c++ -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify -fblocks -triple x86_64-apple-darwin10.0.0 %s + +#ifdef __cplusplus +extern "C" { +#endif + +void *memset(void *, int, __SIZE_TYPE__); +void *memmove(void *s1, const void *s2, __SIZE_TYPE__ n); +void *memcpy(void *s1, const void *s2, __SIZE_TYPE__ n); + +#ifdef __cplusplus +} +#endif + +void test(id __strong *sip, id __weak *wip, id __autoreleasing *aip, + id __unsafe_unretained *uip, void *ptr) { + // All okay. + memset(sip, 0, 17); + memset(wip, 0, 17); + memset(aip, 0, 17); + memset(uip, 0, 17); + + memcpy(sip, ptr, 17); // expected-warning{{destination for this 'memcpy' call is a pointer to lifetime-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memcpy(wip, ptr, 17); // expected-warning{{destination for this 'memcpy' call is a pointer to lifetime-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memcpy(aip, ptr, 17); // expected-warning{{destination for this 'memcpy' call is a pointer to lifetime-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memcpy(uip, ptr, 17); + + memcpy(ptr, sip, 17); // expected-warning{{source of this 'memcpy' call is a pointer to lifetime-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memcpy(ptr, wip, 17); // expected-warning{{source of this 'memcpy' call is a pointer to lifetime-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memcpy(ptr, aip, 17); // expected-warning{{source of this 'memcpy' call is a pointer to lifetime-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memcpy(ptr, uip, 17); + + memmove(sip, ptr, 17); // expected-warning{{destination for this 'memmove' call is a pointer to lifetime-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memmove(wip, ptr, 17); // expected-warning{{destination for this 'memmove' call is a pointer to lifetime-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memmove(aip, ptr, 17); // expected-warning{{destination for this 'memmove' call is a pointer to lifetime-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memmove(uip, ptr, 17); + + memmove(ptr, sip, 17); // expected-warning{{source of this 'memmove' call is a pointer to lifetime-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memmove(ptr, wip, 17); // expected-warning{{source of this 'memmove' call is a pointer to lifetime-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memmove(ptr, aip, 17); // expected-warning{{source of this 'memmove' call is a pointer to lifetime-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memmove(ptr, uip, 17); +} diff --git a/test/SemaObjC/arc-property-decl-attrs.m b/test/SemaObjC/arc-property-decl-attrs.m new file mode 100644 index 0000000000..4a661ee922 --- /dev/null +++ b/test/SemaObjC/arc-property-decl-attrs.m @@ -0,0 +1,67 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify %s +// rdar://9340606 + +@interface Foo { +@public + id __unsafe_unretained x; + id __weak y; + id __autoreleasing z; // expected-error {{ivars cannot have __autoreleasing lifetime}} +} +@property(strong) id x; +@property(strong) id y; +@property(strong) id z; +@end + +@interface Bar { +@public + id __unsafe_unretained x; + id __weak y; + id __autoreleasing z; // expected-error {{ivars cannot have __autoreleasing lifetime}} +} +@property(retain) id x; +@property(retain) id y; +@property(retain) id z; +@end + +@interface Bas { +@public + id __unsafe_unretained x; + id __weak y; + id __autoreleasing z; // expected-error {{ivars cannot have __autoreleasing lifetime}} +} +@property(copy) id x; +@property(copy) id y; +@property(copy) id z; +@end + +// Errors should start about here :-) + +@interface Bat +@property(strong) __unsafe_unretained id x; // expected-error {{strong property 'x' may not also be declared __unsafe_unretained}} +@property(strong) __weak id y; // expected-error {{strong property 'y' may not also be declared __weak}} expected-error {{property attributes 'strong' and 'weak' are mutually exclusive}} +@property(strong) __autoreleasing id z; // expected-error {{strong property 'z' may not also be declared __autoreleasing}} +@end + +@interface Bau +@property(retain) __unsafe_unretained id x; // expected-error {{strong property 'x' may not also be declared __unsafe_unretained}} +@property(retain) __weak id y; // expected-error {{strong property 'y' may not also be declared __weak}} expected-error {{property attributes 'retain' and 'weak' are mutually exclusive}} +@property(retain) __autoreleasing id z; // expected-error {{strong property 'z' may not also be declared __autoreleasing}} +@end + +@interface Bav +@property(copy) __unsafe_unretained id x; // expected-error {{strong property 'x' may not also be declared __unsafe_unretained}} +@property(copy) __weak id y; // expected-error {{strong property 'y' may not also be declared __weak}} expected-error {{property attributes 'copy' and 'weak' are mutually exclusive}} +@property(copy) __autoreleasing id z; // expected-error {{strong property 'z' may not also be declared __autoreleasing}} +@end + +@interface Bingo +@property(assign) __unsafe_unretained id x; +@property(assign) __weak id y; // expected-error {{property attributes 'assign' and 'weak' are mutually exclusive}} +@property(assign) __autoreleasing id z; // expected-error {{unsafe_unretained property 'z' may not also be declared __autoreleasing}} +@end + +@interface Batman +@property(unsafe_unretained) __unsafe_unretained id x; +@property(unsafe_unretained) __weak id y; // expected-error {{property attributes 'unsafe_unretained' and 'weak' are mutually exclusive}} +@property(unsafe_unretained) __autoreleasing id z; // expected-error {{unsafe_unretained property 'z' may not also be declared __autoreleasing}} +@end diff --git a/test/SemaObjC/arc-property-lifetime.m b/test/SemaObjC/arc-property-lifetime.m new file mode 100644 index 0000000000..c2571a547b --- /dev/null +++ b/test/SemaObjC/arc-property-lifetime.m @@ -0,0 +1,112 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify %s +// rdar://9340606 + +@interface Foo { +@public + id __unsafe_unretained x; + id __weak y; + id __autoreleasing z; // expected-error {{ivars cannot have __autoreleasing lifetime}} +} +@property(strong) id x; // expected-note {{property declared here}} +@property(strong) id y; // expected-note {{property declared here}} +@property(strong) id z; +@end + +@implementation Foo +@synthesize x; // expected-error {{existing ivar 'x' for strong property 'x' may not be __unsafe_unretained}} +@synthesize y; // expected-error {{existing ivar 'y' for strong property 'y' may not be __weak}} +@synthesize z; // suppressed +@end + +@interface Bar { +@public + id __unsafe_unretained x; + id __weak y; + id __autoreleasing z; // expected-error {{ivars cannot have __autoreleasing lifetime}} +} +@property(retain) id x; // expected-note {{property declared here}} +@property(retain) id y; // expected-note {{property declared here}} +@property(retain) id z; +@end + +@implementation Bar +@synthesize x; // expected-error {{existing ivar 'x' for strong property 'x' may not be __unsafe_unretained}} +@synthesize y; // expected-error {{existing ivar 'y' for strong property 'y' may not be __weak}} +@synthesize z; // suppressed +@end + +@interface Bas { +@public + id __unsafe_unretained x; + id __weak y; + id __autoreleasing z; // expected-error {{ivars cannot have __autoreleasing lifetime}} +} +@property(copy) id x; // expected-note {{property declared here}} +@property(copy) id y; // expected-note {{property declared here}} +@property(copy) id z; +@end + +@implementation Bas +@synthesize x; // expected-error {{existing ivar 'x' for strong property 'x' may not be __unsafe_unretained}} +@synthesize y; // expected-error {{existing ivar 'y' for strong property 'y' may not be __weak}} +@synthesize z; // suppressed +@end + +@interface Bat +@property(strong) __unsafe_unretained id x; // expected-error {{strong property 'x' may not also be declared __unsafe_unretained}} +@property(strong) __autoreleasing id z; // expected-error {{strong property 'z' may not also be declared __autoreleasing}} +@end + +@interface Bau +@property(retain) __unsafe_unretained id x; // expected-error {{strong property 'x' may not also be declared __unsafe_unretained}} +@property(retain) __autoreleasing id z; // expected-error {{strong property 'z' may not also be declared __autoreleasing}} +@end + +@interface Bav +@property(copy) __unsafe_unretained id x; // expected-error {{strong property 'x' may not also be declared __unsafe_unretained}} +@property(copy) __autoreleasing id z; // expected-error {{strong property 'z' may not also be declared __autoreleasing}} +@end + +// rdar://9341593 +@interface Gorf { + id __unsafe_unretained x; + id y; +} +@property(assign) id __unsafe_unretained x; +@property(assign) id y; // expected-note {{property declared here}} +@property(assign) id z; +@end + +@implementation Gorf +@synthesize x; +@synthesize y; // expected-error {{existing ivar 'y' for unsafe_unretained property 'y' must be __unsafe_unretained}} +@synthesize z; +@end + +@interface Gorf2 { + id __unsafe_unretained x; + id y; +} +@property(unsafe_unretained) id __unsafe_unretained x; +@property(unsafe_unretained) id y; // expected-note {{property declared here}} +@property(unsafe_unretained) id z; +@end + +@implementation Gorf2 +@synthesize x; +@synthesize y; // expected-error {{existing ivar 'y' for unsafe_unretained property 'y' must be __unsafe_unretained}} +@synthesize z; +@end + +// rdar://9355230 +@interface I { + char _isAutosaving; +} +@property char isAutosaving; + +@end + +@implementation I +@synthesize isAutosaving = _isAutosaving; +@end + diff --git a/test/SemaObjC/arc-property.m b/test/SemaObjC/arc-property.m new file mode 100644 index 0000000000..641ae1f911 --- /dev/null +++ b/test/SemaObjC/arc-property.m @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -fblocks -verify %s +// rdar://9309489 + +@interface MyClass { + id __weak myString; + id StrongIvar; + id __weak myString2; + id __weak myString3; + id StrongIvar5; +} +@property (strong) id myString; // expected-note {{property declared here}} +@property (strong) id myString1; +@property (retain) id myString2; // expected-note {{property declared here}} +// +@property (weak) id myString3; +@property (weak) id myString4; +@property __weak id myString5; // expected-note {{property declared here}} +@end + +@implementation MyClass +@synthesize myString; // expected-error {{existing ivar 'myString' for strong property 'myString' may not be __weak}} +@synthesize myString1 = StrongIvar; // OK +@synthesize myString2 = myString2; // expected-error {{existing ivar 'myString2' for strong property 'myString2' may not be __weak}} +// +@synthesize myString3; // OK +@synthesize myString4; // OK +@synthesize myString5 = StrongIvar5; // expected-error {{existing ivar 'StrongIvar5' for __weak property 'myString5' must be __weak}} + +@end + +// rdar://9340692 +@interface Foo { +@public + id __unsafe_unretained x; // should be __weak + id __strong y; + id __autoreleasing z; // expected-error {{ivars cannot have __autoreleasing lifetime}} +} +@property(weak) id x; // expected-note {{property declared here}} +@property(weak) id y; // expected-note {{property declared here}} +@property(weak) id z; +@end + +@implementation Foo +@synthesize x; // expected-error {{existing ivar 'x' for __weak property 'x' must be __weak}} +@synthesize y; // expected-error {{existing ivar 'y' for __weak property 'y' must be __weak}} +@synthesize z; // suppressed +@end + diff --git a/test/SemaObjC/arc-system-header.m b/test/SemaObjC/arc-system-header.m new file mode 100644 index 0000000000..e635dc0a80 --- /dev/null +++ b/test/SemaObjC/arc-system-header.m @@ -0,0 +1,48 @@ +// silly workaround expected-note {{marked unavailable here}} +// RUN: %clang_cc1 -fobjc-arc -fobjc-nonfragile-abi -isystem %S/Inputs %s -DNO_USE +// RUN: %clang_cc1 -fobjc-arc -fobjc-nonfragile-abi -isystem %S/Inputs %s -verify + +// another silly workaround expected-note {{marked unavailable here}} +#include + +#ifndef NO_USE +void test(id op, void *cp) { + cp = test0(op); // expected-error {{'test0' is unavailable: converts between Objective-C and C pointers in -fobjc-arc}} + cp = *test1(&op); // expected-error {{'test1' is unavailable: converts between Objective-C and C pointers in -fobjc-arc}} +} + +// workaround expected-note {{marked unavailable here}} +void test3(struct Test3 *p) { + p->field = 0; // expected-error {{'field' is unavailable: this system declaration uses an unsupported type}} +} + +// workaround expected-note {{marked unavailable here}} +void test4(Test4 *p) { + p->field1 = 0; // expected-error {{'field1' is unavailable: this system declaration uses an unsupported type}} + p->field2 = 0; +} + +// workaround expected-note {{marked unavailable here}} +void test5(struct Test5 *p) { + p->field = 0; // expected-error {{'field' is unavailable: this system field has retaining lifetime}} +} + +id test6() { + // This is actually okay to use if declared in a system header. + id x; + x = (id) kMagicConstant; + x = (id) (x ? kMagicConstant : kMagicConstant); + x = (id) (x ? kMagicConstant : (void*) 0); + + extern void test6_helper(); + x = (id) (test6_helper(), kMagicConstant); +} + +// workaround expected-note 4 {{marked unavailable here}} +void test7(Test7 *p) { + *p.prop = 0; // expected-error {{'prop' is unavailable: this system declaration uses an unsupported type}} + p.prop = 0; // expected-error {{'prop' is unavailable: this system declaration uses an unsupported type}} + *[p prop] = 0; // expected-error {{'prop' is unavailable: this system declaration uses an unsupported type}} + [p setProp: 0]; // expected-error {{'setProp:' is unavailable: this system declaration uses an unsupported type}} +} +#endif diff --git a/test/SemaObjC/arc-type-conversion.m b/test/SemaObjC/arc-type-conversion.m new file mode 100644 index 0000000000..e723a63317 --- /dev/null +++ b/test/SemaObjC/arc-type-conversion.m @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -fobjc-nonfragile-abi -verify %s + +void * cvt(id arg) +{ + void* voidp_val; + (void)(int*)arg; // expected-error {{cast of an Objective-C pointer to 'int *' is disallowed with ARC}} + (void)(id)arg; + (void)(__autoreleasing id*)arg; // expected-error {{cast of an Objective-C pointer to '__autoreleasing id *' is disallowed with ARC}} + (void)(id*)arg; // expected-error {{pointer to non-const type 'id' with no explicit lifetime}} expected-error {{cast of an Objective-C pointer to '__autoreleasing id *' is disallowed with ARC}} + + (void)(__autoreleasing id**)voidp_val; + (void)(void*)voidp_val; + (void)(void**)arg; // expected-error {{cast of an Objective-C pointer to 'void **' is disallowed with ARC}} + cvt((void*)arg); // expected-error {{cast of Objective-C pointer type 'id' to C pointer type 'void *' requires a bridged cast}} \ + // expected-error {{implicit conversion of a non-Objective-C pointer type 'void *' to 'id' is disallowed with ARC}} \ + // expected-note{{use __bridge to convert directly (no change in ownership)}} \ + // expected-note{{use __bridge_retained to make an ARC object available as a +1 'void *'}} + cvt(0); + (void)(__strong id**)(0); + return arg; // expected-error {{implicit conversion of an Objective-C pointer to 'void *' is disallowed with ARC}} +} + +void to_void(__strong id *sip, __weak id *wip, + __autoreleasing id *aip, + __unsafe_unretained id *uip) { + void *vp1 = sip; + void *vp2 = wip; + void *vp3 = aip; + void *vp4 = uip; + (void)(void*)sip; + (void)(void*)wip; + (void)(void*)aip; + (void)(void*)uip; + (void)(void*)&sip; + (void)(void*)&wip; + (void)(void*)&aip; + (void)(void*)&uip; +} + +void from_void(void *vp) { + __strong id *sip = (__strong id *)vp; + __weak id *wip = (__weak id *)vp; + __autoreleasing id *aip = (__autoreleasing id *)vp; + __unsafe_unretained id *uip = (__unsafe_unretained id *)vp; + + __strong id **sipp = (__strong id **)vp; + __weak id **wipp = (__weak id **)vp; + __autoreleasing id **aipp = (__autoreleasing id **)vp; + __unsafe_unretained id **uipp = (__unsafe_unretained id **)vp; + + sip = vp; // expected-error{{implicit conversion of a non-Objective-C pointer type 'void *' to '__strong id *' is disallowed with ARC}} + wip = vp; // expected-error{{implicit conversion of a non-Objective-C pointer type 'void *' to '__weak id *' is disallowed with ARC}} + aip = vp; // expected-error{{implicit conversion of a non-Objective-C pointer type 'void *' to '__autoreleasing id *' is disallowed with ARC}} + uip = vp; // expected-error{{implicit conversion of a non-Objective-C pointer type 'void *' to '__unsafe_unretained id *' is disallowed with ARC}} +} diff --git a/test/SemaObjC/arc-unsafe_unretained.m b/test/SemaObjC/arc-unsafe_unretained.m new file mode 100644 index 0000000000..20ae792d5d --- /dev/null +++ b/test/SemaObjC/arc-unsafe_unretained.m @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -fblocks -fobjc-nonfragile-abi %s +// RUN: %clang_cc1 -fsyntax-only -verify -fblocks -fobjc-nonfragile-abi -fobjc-arc %s + +struct X { + __unsafe_unretained id object; + int (^ __unsafe_unretained block)(int, int); +}; + +void f(struct X x) { + x.object = 0; + x.block = ^(int x, int y) { return x + y; }; +} diff --git a/test/SemaObjC/arc.m b/test/SemaObjC/arc.m new file mode 100644 index 0000000000..fa3a06183e --- /dev/null +++ b/test/SemaObjC/arc.m @@ -0,0 +1,550 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -fblocks -verify %s + +typedef unsigned long NSUInteger; + +void test0(void (*fn)(int), int val) { + fn(val); +} + +@interface A +- (id)retain; +- (id)autorelease; +- (oneway void)release; +- (void)dealloc; +- (NSUInteger)retainCount; +@end + +void test1(A *a) { + SEL s = @selector(retain); // expected-error {{ARC forbids use of 'retain' in a @selector}} + s = @selector(release); // expected-error {{ARC forbids use of 'release' in a @selector}} + s = @selector(autorelease); // expected-error {{ARC forbids use of 'autorelease' in a @selector}} + s = @selector(dealloc); // expected-error {{ARC forbids use of 'dealloc' in a @selector}} + [a dealloc]; // expected-error {{ARC forbids explicit message send of 'dealloc'}} + [a retain]; // expected-error {{ARC forbids explicit message send of 'retain'}} + [a retainCount]; // expected-error {{ARC forbids explicit message send of 'retainCount'}} + [a release]; // expected-error {{ARC forbids explicit message send of 'release'}} + [a autorelease]; // expected-error {{ARC forbids explicit message send of 'autorelease'}} +} + +@interface Test2 : A +- (void) dealloc; +@end +@implementation Test2 +- (void) dealloc { + // This should maybe just be ignored. We're just going to warn about it for now. + [super dealloc]; // expected-error {{ARC forbids explicit message send of 'dealloc'}} +} +@end + +__weak __strong id x; // expected-error {{the type '__strong id' already has retainment attributes}} + +// rdar://8843638 + +@interface I +- (id)retain; +- (id)autorelease; +- (oneway void)release; +- (NSUInteger)retainCount; +@end + +@implementation I +- (id)retain{return 0;} // expected-error {{ARC forbids implementation of 'retain'}} +- (id)autorelease{return 0;} // expected-error {{ARC forbids implementation of 'autorelease'}} +- (oneway void)release{} // expected-error {{ARC forbids implementation of 'release'}} +- (NSUInteger)retainCount{ return 0; } // expected-error {{ARC forbids implementation of 'retainCount'}} +@end + +@implementation I(CAT) +- (id)retain{return 0;} // expected-error {{ARC forbids implementation of 'retain'}} +- (id)autorelease{return 0;} // expected-error {{ARC forbids implementation of 'autorelease'}} +- (oneway void)release{} // expected-error {{ARC forbids implementation of 'release'}} +- (NSUInteger)retainCount{ return 0; } // expected-error {{ARC forbids implementation of 'retainCount'}} +@end + +// rdar://8861761 + +@interface B +-(id)alloc; +- (id)initWithInt: (int) i; +@end + +void rdar8861761() { + B *o1 = [[B alloc] initWithInt:0]; + B *o2 = [B alloc]; + [o2 initWithInt:0]; // expected-warning {{expression result unused}} +} + +// rdar://8925835 +@interface rdar8925835 +- (void)foo:(void (^)(unsigned captureCount, I * const capturedStrings[captureCount]))block; +@end + +void test5() { + extern void test5_helper(__autoreleasing id *); + id x; + + // Okay because of magic temporaries. + test5_helper(&x); + + __autoreleasing id *a = &x; // expected-error {{initializing '__autoreleasing id *' with an expression of type '__strong id *' changes retain/release properties of pointer}} + + a = &x; // expected-error {{assigning '__strong id *' to '__autoreleasing id *' changes retain/release properties of pointer}} + + extern void test5_helper2(id const *); + test5_helper2(&x); + + extern void test5_helper3(__weak id *); // expected-note {{passing argument to parameter here}} + test5_helper3(&x); // expected-error {{passing '__strong id *' to parameter of type '__weak id *' changes retain/release properties of pointer}} +} + +// rdar://problem/8937869 +void test6(unsigned cond) { + switch (cond) { + case 0: + ; + id x; // expected-note {{jump bypasses initialization of retaining variable}} + + case 1: // expected-error {{switch case is in protected scope}} + break; + } +} + +@class NSError; +void test7(void) { + extern void test7_helper(NSError **); + NSError *err; + test7_helper(&err); +} +void test7_weak(void) { + extern void test7_helper(NSError **); + __weak NSError *err; + test7_helper(&err); +} +void test7_unsafe(void) { + extern void test7_helper(NSError **); // expected-note {{passing argument to parameter here}} + __unsafe_unretained NSError *err; + test7_helper(&err); // expected-error {{passing 'NSError *__unsafe_unretained *' to parameter of type 'NSError *__autoreleasing *' changes retain/release properties of pointer}} +} + +@class Test8_incomplete; +@interface Test8_complete @end; +@interface Test8_super @end; +@interface Test8 : Test8_super +- (id) init00; +- (id) init01; // expected-note {{declaration in interface}} \ + // expected-note{{overridden method}} +- (id) init02; // expected-note{{overridden method}} +- (id) init03; // covariance +- (id) init04; // covariance +- (id) init05; // expected-note{{overridden method}} + +- (void) init10; // expected-note {{declaration in interface is not in the 'init' family because its result type is not an object pointer}} +- (void) init11; +- (void) init12; +- (void) init13; // expected-note {{declaration in interface is not in the 'init' family because its result type is not an object pointer}} +- (void) init14; // expected-note {{declaration in interface is not in the 'init' family because its result type is not an object pointer}} +- (void) init15; + +// These should be invalid to actually call. +- (Test8_incomplete*) init20; +- (Test8_incomplete*) init21; // expected-note {{declaration in interface}} +- (Test8_incomplete*) init22; +- (Test8_incomplete*) init23; +- (Test8_incomplete*) init24; +- (Test8_incomplete*) init25; + +- (Test8_super*) init30; // id exception to covariance +- (Test8_super*) init31; // expected-note {{declaration in interface}} \ + // expected-note{{overridden method}} +- (Test8_super*) init32; // expected-note{{overridden method}} +- (Test8_super*) init33; +- (Test8_super*) init34; // covariance +- (Test8_super*) init35; // expected-note{{overridden method}} + +- (Test8*) init40; // id exception to covariance +- (Test8*) init41; // expected-note {{declaration in interface}} \ + // expected-note{{overridden method}} +- (Test8*) init42; // expected-note{{overridden method}} +- (Test8*) init43; // this should be a warning, but that's a general language thing, not an ARC thing +- (Test8*) init44; +- (Test8*) init45; // expected-note{{overridden method}} + +- (Test8_complete*) init50; // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init51; // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init52; // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init53; // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init54; // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init55; // expected-error {{init methods must return a type related to the receiver type}} +@end +@implementation Test8 +- (id) init00 { return 0; } +- (id) init10 { return 0; } // expected-error {{method implementation does not match its declaration}} +- (id) init20 { return 0; } +- (id) init30 { return 0; } +- (id) init40 { return 0; } +- (id) init50 { return 0; } + +- (void) init01 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}} \ + // expected-warning{{ method is expected to return an instance of its class type 'Test8', but is declared to return 'void'}} +- (void) init11 {} +- (void) init21 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}} +- (void) init31 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}} \ + // expected-warning{{ method is expected to return an instance of its class type 'Test8', but is declared to return 'void'}} +- (void) init41 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}} \ + // expected-warning{{ method is expected to return an instance of its class type 'Test8', but is declared to return 'void'}} +- (void) init51 {} + +- (Test8_incomplete*) init02 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} \ + // expected-warning{{method is expected to return an instance of its class type 'Test8', but is declared to return 'Test8_incomplete *'}} +- (Test8_incomplete*) init12 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_incomplete*) init22 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_incomplete*) init32 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} \ + // expected-warning{{method is expected to return an instance of its class type 'Test8', but is declared to return 'Test8_incomplete *'}} +- (Test8_incomplete*) init42 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} \ + // expected-warning{{method is expected to return an instance of its class type 'Test8', but is declared to return 'Test8_incomplete *'}} +- (Test8_incomplete*) init52 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} + +- (Test8_super*) init03 { return 0; } +- (Test8_super*) init13 { return 0; } // expected-error {{method implementation does not match its declaration}} +- (Test8_super*) init23 { return 0; } +- (Test8_super*) init33 { return 0; } +- (Test8_super*) init43 { return 0; } +- (Test8_super*) init53 { return 0; } + +- (Test8*) init04 { return 0; } +- (Test8*) init14 { return 0; } // expected-error {{method implementation does not match its declaration}} +- (Test8*) init24 { return 0; } +- (Test8*) init34 { return 0; } +- (Test8*) init44 { return 0; } +- (Test8*) init54 { return 0; } + +- (Test8_complete*) init05 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} \ + // expected-warning{{method is expected to return an instance of its class type 'Test8', but is declared to return 'Test8_complete *'}} +- (Test8_complete*) init15 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init25 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init35 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} \ + // expected-warning{{method is expected to return an instance of its class type 'Test8', but is declared to return 'Test8_complete *'}} +- (Test8_complete*) init45 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} \ + // expected-warning{{method is expected to return an instance of its class type 'Test8', but is declared to return 'Test8_complete *'}} +- (Test8_complete*) init55 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +@end + +@class Test9_incomplete; +@interface Test9 +- (Test9_incomplete*) init1; // expected-error {{init methods must return a type related to the receiver type}} +- (Test9_incomplete*) init2; +@end +id test9(Test9 *v) { + return [v init1]; +} + +// Test that the inference rules are different for fast enumeration variables. +void test10(id collection) { + for (id x in collection) { + __strong id *ptr = &x; // expected-error {{initializing '__strong id *' with an expression of type 'const __unsafe_unretained id *' changes retain/release properties of pointer}} + } + + for (__strong id x in collection) { + __weak id *ptr = &x; // expected-error {{initializing '__weak id *' with an expression of type '__strong id *' changes retain/release properties of pointer}} + } +} + +// rdar://problem/9078626 +#define nil ((void*) 0) +void test11(id op, void *vp) { + _Bool b; + b = (op == nil); + b = (nil == op); + + b = (vp == nil); + b = (nil == vp); + + b = (vp == op); // expected-error {{implicit conversion of an Objective-C pointer to 'void *'}} + b = (op == vp); // expected-error {{implicit conversion of a non-Objective-C pointer type 'void *' to 'id'}} +} + +void test12(id collection) { + for (id x in collection) { + x = 0; // expected-error {{fast enumeration variables can't be modified in ARC by default; declare the variable __strong to allow this}} + } + + for (const id x in collection) { + x = 0; // expected-error {{read-only variable is not assignable}} + } + + for (__strong id x in collection) { + x = 0; + } +} + +@interface Test13 +- (id) init0; +- (void) noninit; +@end +@implementation Test13 +- (id) init0 { + self = 0; +} +- (void) noninit { + self = 0; // expected-error {{cannot assign to 'self' outside of a method in the init family}} +} +@end + +// rdar://problem/9172151 +@class Test14A, Test14B; +void test14() { + extern void test14_consume(id *); + extern int test14_cond(void); + extern float test14_nowriteback(id __autoreleasing const *); // expected-note{{passing argument to parameter here}} + + Test14A *a; + Test14B *b; + id i; + id cla[10]; + id vla[test14_cond() + 10]; + + test14_consume((__strong id*) &a); + test14_consume((test14_cond() ? (__strong id*) &b : &i)); + test14_consume(test14_cond() ? 0 : &a); + test14_consume(test14_cond() ? (void*) 0 : (&a)); + test14_consume(cla); // expected-error {{passing address of non-scalar object to __autoreleasing parameter for write-back}} + test14_consume(vla); // expected-error {{passing address of non-scalar object to __autoreleasing parameter for write-back}} + test14_consume(&cla[5]); // expected-error {{passing address of non-scalar object to __autoreleasing parameter for write-back}} + + __strong id *test14_indirect(void); + test14_consume(test14_indirect()); // expected-error {{passing address of non-local object to __autoreleasing parameter for write-back}} + + extern id test14_global; + test14_consume(&test14_global); // expected-error {{passing address of non-local object to __autoreleasing parameter for write-back}} + + extern __strong id *test14_global_ptr; + test14_consume(test14_global_ptr); // expected-error {{passing address of non-local object to __autoreleasing parameter for write-back}} + + static id static_local; + test14_consume(&static_local); // expected-error {{passing address of non-local object to __autoreleasing parameter for write-back}} + + __weak id* wip; + test14_nowriteback(&static_local); // okay, not a write-back. + test14_nowriteback(wip); // expected-error{{passing '__weak id *' to parameter of type '__autoreleasing id const *' changes retain/release properties of pointer}} +} + +void test15() { + __block __autoreleasing id x; // expected-error {{__block variables cannot have __autoreleasing lifetime}} +} + +struct Test16; +@interface Test16a +- (void) test16_0: (int) x; +- (int) test16_1: (int) x; // expected-note {{one possibility}} +- (int) test16_2: (int) x; // expected-note {{one possibility}} +- (id) test16_3: (int) x __attribute__((ns_returns_retained)); // expected-note {{one possibility}} +- (void) test16_4: (int) x __attribute__((ns_consumes_self)); // expected-note {{one possibility}} +- (void) test16_5: (id) __attribute__((ns_consumed)) x; // expected-note {{one possibility}} +- (void) test16_6: (id) x; +@end + +@interface Test16b +- (void) test16_0: (int) x; +- (int) test16_1: (char*) x; // expected-note {{also found}} +- (char*) test16_2: (int) x; // expected-note {{also found}} +- (id) test16_3: (int) x; // expected-note {{also found}} +- (void) test16_4: (int) x; // expected-note {{also found}} +- (void) test16_5: (id) x; // expected-note {{also found}} +- (void) test16_6: (struct Test16 *) x; +@end + +void test16(void) { + id v; + [v test16_0: 0]; + [v test16_1: 0]; // expected-error {{multiple methods named 'test16_1:' found with mismatched result, parameter type or attributes}} + [v test16_2: 0]; // expected-error {{multiple methods named}} + [v test16_3: 0]; // expected-error {{multiple methods named}} + [v test16_4: 0]; // expected-error {{multiple methods named}} + [v test16_5: 0]; // expected-error {{multiple methods named}} + [v test16_6: 0]; +} + +@class Test17; +@protocol Test17p +- (void) test17; ++ (void) test17; +@end +void test17(void) { + Test17 *v0; + [v0 test17]; // expected-error {{receiver type 'Test17' for instance message is a forward declaration}} + + Test17 *v1; + [v1 test17]; // expected-error {{receiver type 'Test17' for instance message is a forward declaration}} + + [Test17 test17]; // expected-error {{receiver 'Test17' for class message is a forward declaration}} +} + +void test18(void) { + id x; + [x test18]; // expected-error {{no known instance method for selector 'test18'}} +} + +extern struct Test19 *test19a; +struct Test19 *const test19b = 0; +void test19(void) { + id x; + x = (id) test19a; // expected-error {{bridged cast}} \ + // expected-note{{use __bridge to convert directly (no change in ownership))}} \ + // expected-note{{use __bridge_transfer to transfer ownership of a +1 'struct Test19 *' into ARC}} + x = (id) test19b; // expected-error {{bridged cast}} \ + // expected-note{{use __bridge to convert directly (no change in ownership)}} \ + // expected-note{{use __bridge_transfer to transfer ownership of a +1 'struct Test19 *' into ARC}} +} + +// rdar://problem/8951453 +static __thread id test20_implicit; // expected-error {{thread-local variable has non-trivial lifetime: type is '__strong id'}} +static __thread __strong id test20_strong; // expected-error {{thread-local variable has non-trivial lifetime: type is '__strong id'}} +static __thread __weak id test20_weak; // expected-error {{thread-local variable has non-trivial lifetime: type is '__weak id'}} +static __thread __autoreleasing id test20_autoreleasing; // expected-error {{thread-local variable has non-trivial lifetime: type is '__autoreleasing id'}} expected-error {{global variables cannot have __autoreleasing lifetime}} +static __thread __unsafe_unretained id test20_unsafe; +void test20(void) { + static __thread id test20_implicit; // expected-error {{thread-local variable has non-trivial lifetime: type is '__strong id'}} + static __thread __strong id test20_strong; // expected-error {{thread-local variable has non-trivial lifetime: type is '__strong id'}} + static __thread __weak id test20_weak; // expected-error {{thread-local variable has non-trivial lifetime: type is '__weak id'}} + static __thread __autoreleasing id test20_autoreleasing; // expected-error {{thread-local variable has non-trivial lifetime: type is '__autoreleasing id'}} expected-error {{global variables cannot have __autoreleasing lifetime}} + static __thread __unsafe_unretained id test20_unsafe; +} + +// rdar://9310049 +_Bool fn(id obj) { + return (_Bool)obj; +} + +// Check casting w/ lifetime qualifiers. +void test21() { + __strong id *sip; + (void)(__weak id *)sip; // expected-error{{casting '__strong id *' to type '__weak id *' changes retain/release properties of pointer}} + (void)(__weak const id *)sip; // expected-error{{casting '__strong id *' to type '__weak id const *' changes retain/release properties of pointer}} + (void)(__autoreleasing id *)sip; // expected-error{{casting '__strong id *' to type '__autoreleasing id *' changes retain/release properties of pointer}} + (void)(__autoreleasing const id *)sip; // okay +} + +// rdar://problem/9340462 +void test22(id x[]) { // expected-error {{must explicitly describe intended lifetime of an object array parameter}} +} + +// rdar://problem/9400219 +void test23(void) { + void *ptr; + ptr = @"foo"; + ptr = (ptr ? @"foo" : 0); + ptr = (ptr ? @"foo" : @"bar"); +} + +id test24(void) { + extern void test24_helper(void); + return test24_helper(), (void*) 0; +} + +// rdar://9400841 +@interface Base +@property (assign) id content; +@end + +@interface Foo : Base +-(void)test; +@end + +@implementation Foo +-(void)test { + super.content = 0; +} +@end + +// +void test25(Class *classes) { + Class *other_classes; + test25(other_classes); +} + +void test26(id y) { + extern id test26_var1; + __sync_swap(&test26_var1, 0, y); // expected-error {{cannot perform atomic operation on a pointer to type '__strong id': type has non-trivial lifetime}} + + extern __unsafe_unretained id test26_var2; + __sync_swap(&test26_var2, 0, y); +} + +@interface Test26 +- (id) init; +- (id) initWithInt: (int) x; +@end +@implementation Test26 +- (id) init { return self; } +- (id) initWithInt: (int) x { + [self init]; // expected-error {{the result of a delegate init call must be immediately returned or assigned to 'self'}} + return self; +} +@end + +// rdar://9525555 +@interface Test27 +@property id x; // expected-error {{ARC forbids properties of Objective-C objects with unspecified storage attribute}} +@property int y; +@end + +// rdar://9569264 +@interface Test28 +@property (nonatomic, assign) __strong id a; // expected-error {{unsafe_unretained property 'a' may not also be declared __strong}} +@end + +@interface Test28 () +@property (nonatomic, assign) __strong id b; // expected-error {{unsafe_unretained property 'b' may not also be declared __strong}} +@end + +@implementation Test28 +@synthesize a; +@synthesize b; +@end + +// rdar://9573962 +typedef struct Bark Bark; +@interface Test29 +@property Bark* P; +@end + +@implementation Test29 +@synthesize P; +- (id)Meth { + Bark** f = &P; + return 0; +} +@end + +// rdar://9495837 +@interface Test30 +- (id) new; +- (void)Meth; +@end + +@implementation Test30 +- (id) new { return 0; } +- (void) Meth { + __weak id x = [Test30 new]; // expected-warning {{cannot assign retained object to weak variable}} + id __unsafe_unretained u = [Test30 new]; // expected-warning {{cannot assign retained object to unsafe_unretained variable}} + id y = [Test30 new]; + x = [Test30 new]; // expected-warning {{cannot assign retained object to weak variable}} + u = [Test30 new]; // expected-warning {{cannot assign retained object to unsafe_unretained variable}} + y = [Test30 new]; +} +@end + +// rdar://9411838 +@protocol PTest31 @end + +int Test31() { + Class cls; + id ids; + id pids; + Class pcls; + + int i = (ids->isa ? 1 : 0); // expected-error {{member reference base type 'id' is not a structure or union}} + int j = (pids->isa ? 1 : 0); // expected-error {{member reference base type 'id' is not a structure or union}} + int k = (pcls->isa ? i : j); // expected-error {{member reference base type 'Class' is not a structure or union}} + return cls->isa ? i : j; // expected-error {{member reference base type 'Class' is not a structure or union}} +} diff --git a/test/SemaObjC/autoreleasepool.m b/test/SemaObjC/autoreleasepool.m new file mode 100644 index 0000000000..41e17681af --- /dev/null +++ b/test/SemaObjC/autoreleasepool.m @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +void *objc_autoreleasepool_push(); +void autoreleasepool_pop(void*); + +@interface AUTORP @end + +@implementation AUTORP +- (void) unregisterTask:(id) task { + goto L; // expected-error {{goto into protected scope}} + + @autoreleasepool { // expected-note {{jump bypasses auto release push of @autoreleasepool block}} + void *tmp = objc_autoreleasepool_push(); + L: + autoreleasepool_pop(tmp); + @autoreleasepool { + return; + } + } +} +@end + diff --git a/test/SemaObjC/class-unavail-warning.m b/test/SemaObjC/class-unavail-warning.m index 426ac772c8..408647ac11 100644 --- a/test/SemaObjC/class-unavail-warning.m +++ b/test/SemaObjC/class-unavail-warning.m @@ -2,7 +2,7 @@ // rdar://9092208 __attribute__((unavailable("not available"))) -@interface MyClass { // expected-note 5 {{function has been explicitly marked unavailable here}} +@interface MyClass { // expected-note 5 {{declaration has been explicitly marked unavailable here}} @public void *_test; } diff --git a/test/SemaObjC/error-property-gc-attr.m b/test/SemaObjC/error-property-gc-attr.m index a77b68bff0..829c082287 100644 --- a/test/SemaObjC/error-property-gc-attr.m +++ b/test/SemaObjC/error-property-gc-attr.m @@ -20,9 +20,9 @@ @implementation INTF @synthesize pweak=IVAR; // expected-error {{existing ivar 'IVAR' for __weak property 'pweak' must be __weak}} -@synthesize NOT=II; // expected-error {{property 'NOT' must be declared __weak to match existing ivar 'II' with __weak attribute}} +@synthesize NOT=II; // expected-error {{existing ivar 'II' for strong property 'NOT' may not be __weak}} @synthesize WID; @synthesize ID; -@synthesize AWEAK; // expected-error {{property 'AWEAK' must be declared __weak to match existing ivar 'AWEAK' with __weak attribute}} +@synthesize AWEAK; // expected-error {{existing ivar 'AWEAK' for strong property 'AWEAK' may not be __weak}} @synthesize WI; @end diff --git a/test/SemaObjC/property-10.m b/test/SemaObjC/property-10.m index 011927368f..e89d68e9d0 100644 --- a/test/SemaObjC/property-10.m +++ b/test/SemaObjC/property-10.m @@ -5,14 +5,24 @@ @interface I0 @property(readonly, readwrite) int p0; // expected-error {{property attributes 'readonly' and 'readwrite' are mutually exclusive}} -@property(retain) int p1; // expected-error {{property with 'retain' attribute must be of object type}} +@property(retain) int p1; // expected-error {{property with 'retain (or strong)' attribute must be of object type}} +@property(strong) int s1; // expected-error {{property with 'retain (or strong)' attribute must be of object type}} @property(copy) int p2; // expected-error {{property with 'copy' attribute must be of object type}} @property(assign, copy) id p3_0; // expected-error {{property attributes 'assign' and 'copy' are mutually exclusive}} @property(assign, retain) id p3_1; // expected-error {{property attributes 'assign' and 'retain' are mutually exclusive}} +@property(assign, strong) id s3_1; // expected-error {{property attributes 'assign' and 'strong' are mutually exclusive}} @property(copy, retain) id p3_2; // expected-error {{property attributes 'copy' and 'retain' are mutually exclusive}} +@property(copy, strong) id s3_2; // expected-error {{property attributes 'copy' and 'strong' are mutually exclusive}} @property(assign, copy, retain) id p3_3; // expected-error {{property attributes 'assign' and 'copy' are mutually exclusive}}, expected-error {{property attributes 'assign' and 'retain' are mutually exclusive}} +@property(assign, copy, strong) id s3_3; // expected-error {{property attributes 'assign' and 'copy' are mutually exclusive}}, expected-error {{property attributes 'assign' and 'strong' are mutually exclusive}} + +@property(unsafe_unretained, copy) id p4_0; // expected-error {{property attributes 'unsafe_unretained' and 'copy' are mutually exclusive}} +@property(unsafe_unretained, retain) id p4_1; // expected-error {{property attributes 'unsafe_unretained' and 'retain' are mutually exclusive}} +@property(unsafe_unretained, strong) id s4_1; // expected-error {{property attributes 'unsafe_unretained' and 'strong' are mutually exclusive}} +@property(unsafe_unretained, copy, retain) id p4_3; // expected-error {{property attributes 'unsafe_unretained' and 'copy' are mutually exclusive}}, expected-error {{property attributes 'unsafe_unretained' and 'retain' are mutually exclusive}} +@property(unsafe_unretained, copy, strong) id s4_3; // expected-error {{property attributes 'unsafe_unretained' and 'copy' are mutually exclusive}}, expected-error {{property attributes 'unsafe_unretained' and 'strong' are mutually exclusive}} @property id p4; // expected-warning {{no 'assign', 'retain', or 'copy' attribute is specified - 'assign' is assumed}}, expected-warning {{default property attribute 'assign' not appropriate for non-gc object}} @@ -22,7 +32,8 @@ @end @interface I0() -@property (retain) int PROP; // expected-error {{property with 'retain' attribute must be of object type}} +@property (retain) int PROP; // expected-error {{property with 'retain (or strong)' attribute must be of object type}} +@property (strong) int SPROP; // expected-error {{property with 'retain (or strong)' attribute must be of object type}} @property(nonatomic,copy) int (*PROP1)(); // expected-error {{property with 'copy' attribute must be of object type}} +@property(nonatomic,weak) int (*PROP2)(); // expected-error {{property with 'weak' attribute must be of object type}} @end - diff --git a/test/SemaObjC/special-dep-unavail-warning.m b/test/SemaObjC/special-dep-unavail-warning.m index 1d07a49e61..57a2fa3465 100644 --- a/test/SemaObjC/special-dep-unavail-warning.m +++ b/test/SemaObjC/special-dep-unavail-warning.m @@ -3,24 +3,24 @@ @interface B - (void) depInA; -- (void) unavailMeth __attribute__((unavailable)); // expected-note {{function has been explicitly marked unavailable here}} +- (void) unavailMeth __attribute__((unavailable)); // expected-note {{has been explicitly marked unavailable here}} - (void) depInA1 __attribute__((deprecated)); - (void) unavailMeth1; - (void) depInA2 __attribute__((deprecated)); -- (void) unavailMeth2 __attribute__((unavailable)); // expected-note {{function has been explicitly marked unavailable here}} +- (void) unavailMeth2 __attribute__((unavailable)); // expected-note {{has been explicitly marked unavailable here}} - (void) depunavailInA; -- (void) depunavailInA1 __attribute__((deprecated)) __attribute__((unavailable)); // expected-note {{function has been explicitly marked unavailable here}} +- (void) depunavailInA1 __attribute__((deprecated)) __attribute__((unavailable)); // expected-note {{has been explicitly marked unavailable here}} - (void)FuzzyMeth __attribute__((deprecated)); - (void)FuzzyMeth1 __attribute__((unavailable)); @end @interface A -- (void) unavailMeth1 __attribute__((unavailable)); // expected-note {{function has been explicitly marked unavailable here}} +- (void) unavailMeth1 __attribute__((unavailable)); // expected-note {{has been explicitly marked unavailable here}} - (void) depInA __attribute__((deprecated)); - (void) depInA2 __attribute__((deprecated)); - (void) depInA1; - (void) unavailMeth2 __attribute__((unavailable)); -- (void) depunavailInA __attribute__((deprecated)) __attribute__((unavailable)); // expected-note {{function has been explicitly marked unavailable here}} +- (void) depunavailInA __attribute__((deprecated)) __attribute__((unavailable)); // expected-note {{has been explicitly marked unavailable here}} - (void) depunavailInA1; - (void)FuzzyMeth __attribute__((unavailable)); - (void)FuzzyMeth1 __attribute__((deprecated)); diff --git a/test/SemaObjC/synthesized-ivar.m b/test/SemaObjC/synthesized-ivar.m index 3bc372bcd8..4786d808fa 100644 --- a/test/SemaObjC/synthesized-ivar.m +++ b/test/SemaObjC/synthesized-ivar.m @@ -51,3 +51,11 @@ int f0(I *a) { return a->IP; } // expected-error {{instance variable 'IP' is pri } @end +@interface A +@property (weak) id testObjectWeakProperty; // expected-note {{declared here}} +@end + +@implementation A +// rdar://9605088 +@synthesize testObjectWeakProperty; // expected-error {{@synthesize of 'weak' property is only allowed in ARC or GC mode}} +@end diff --git a/test/SemaObjC/warn-retain-cycle.m b/test/SemaObjC/warn-retain-cycle.m new file mode 100644 index 0000000000..fd69ce2e5b --- /dev/null +++ b/test/SemaObjC/warn-retain-cycle.m @@ -0,0 +1,91 @@ +// RUN: %clang_cc1 -fsyntax-only -fobjc-nonfragile-abi -fobjc-arc -fblocks -verify %s + +@interface Test0 +- (void) setBlock: (void(^)(void)) block; +- (void) addBlock: (void(^)(void)) block; +- (void) actNow; +@end +void test0(Test0 *x) { + [x setBlock: // expected-note {{block will be retained by the captured object}} + ^{ [x actNow]; }]; // expected-warning {{capturing 'x' strongly in this block is likely to lead to a retain cycle}} + x.block = // expected-note {{block will be retained by the captured object}} + ^{ [x actNow]; }; // expected-warning {{capturing 'x' strongly in this block is likely to lead to a retain cycle}} + + [x addBlock: // expected-note {{block will be retained by the captured object}} + ^{ [x actNow]; }]; // expected-warning {{capturing 'x' strongly in this block is likely to lead to a retain cycle}} + + // These actually don't cause retain cycles. + __weak Test0 *weakx = x; + [x addBlock: ^{ [weakx actNow]; }]; + [x setBlock: ^{ [weakx actNow]; }]; + x.block = ^{ [weakx actNow]; }; + + // These do cause retain cycles, but we're not clever enough to figure that out. + [weakx addBlock: ^{ [x actNow]; }]; + [weakx setBlock: ^{ [x actNow]; }]; + weakx.block = ^{ [x actNow]; }; +} + +@interface BlockOwner +@property (retain) void (^strong)(void); +@end + +@interface Test1 { +@public + BlockOwner *owner; +}; +@property (retain) BlockOwner *owner; +@property (assign) __strong BlockOwner *owner2; // expected-error {{unsafe_unretained property 'owner2' may not also be declared __strong}} +@property (assign) BlockOwner *owner3; +@end +void test1(Test1 *x) { + x->owner.strong = ^{ (void) x; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} + x.owner.strong = ^{ (void) x; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} + x.owner2.strong = ^{ (void) x; }; + x.owner3.strong = ^{ (void) x; }; +} + +@implementation Test1 { + BlockOwner * __unsafe_unretained owner3ivar; + __weak BlockOwner *weakowner; +} +@dynamic owner; +@dynamic owner2; +@synthesize owner3 = owner3ivar; + +- (id) init { + self.owner.strong = ^{ (void) owner; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} + self.owner2.strong = ^{ (void) owner; }; + + // TODO: should we warn here? What's the story with this kind of mismatch? + self.owner3.strong = ^{ (void) owner; }; + + owner.strong = ^{ (void) owner; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} + + owner.strong = ^{ ^{ (void) owner; }(); }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} + + owner.strong = ^{ (void) sizeof(self); // expected-note {{block will be retained by an object strongly retained by the captured object}} + (void) owner; }; // expected-warning {{capturing 'self' strongly in this block is likely to lead to a retain cycle}} + + weakowner.strong = ^{ (void) owner; }; + + return self; +} +- (void) foo { + owner.strong = ^{ (void) owner; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} +} +@end + +void test2_helper(id); +@interface Test2 { + void (^block)(void); + id x; +} +@end +@implementation Test2 +- (void) test { + block = ^{ // expected-note {{block will be retained by an object strongly retained by the captured object}} + test2_helper(x); // expected-warning {{capturing 'self' strongly in this block is likely to lead to a retain cycle}} + }; +} +@end diff --git a/test/SemaObjC/weak-property.m b/test/SemaObjC/weak-property.m new file mode 100644 index 0000000000..3fabac07a3 --- /dev/null +++ b/test/SemaObjC/weak-property.m @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -fsyntax-only -fobjc-nonfragile-abi -fobjc-arc -verify %s +// rdar://8899430 + +@interface WeakPropertyTest { + Class isa; + __weak id value; + id x; +} +@property (weak) id value1; +@property __weak id value; +@property () __weak id value2; + +@property (weak, assign) id v1; // expected-error {{property attributes 'assign' and 'weak' are mutually exclusive}} +@property (weak, copy) id v2; // expected-error {{property attributes 'copy' and 'weak' are mutually exclusive}} +@property (weak, retain) id v3; // expected-error {{property attributes 'retain' and 'weak' are mutually exclusive}} +@property (weak, assign) id v4; // expected-error {{property attributes 'assign' and 'weak' are mutually exclusive}} + +@property () __weak id x; // expected-note {{property declared here}} +@end + +@implementation WeakPropertyTest +@synthesize x; // expected-error {{existing ivar 'x' for __weak property 'x' must be __weak}} +@dynamic value1, value, value2, v1,v2,v3,v4; +@end diff --git a/test/SemaObjCXX/Inputs/arc-system-header.h b/test/SemaObjCXX/Inputs/arc-system-header.h new file mode 100644 index 0000000000..d7adeb4d40 --- /dev/null +++ b/test/SemaObjCXX/Inputs/arc-system-header.h @@ -0,0 +1,14 @@ +@interface B +@end + + +@interface A { +@public + union { + struct { + B *b; + } a_b; + void *void_ptr; + } data; +} +@end diff --git a/test/SemaObjCXX/arc-0x.mm b/test/SemaObjCXX/arc-0x.mm new file mode 100644 index 0000000000..9b48da5a33 --- /dev/null +++ b/test/SemaObjCXX/arc-0x.mm @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -std=c++0x -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify -fblocks %s + +// "Move" semantics, trivial version. +void move_it(__strong id &&from) { + id to = static_cast<__strong id&&>(from); +} + +// Deduction with 'auto'. +@interface A ++ alloc; +- init; +@end + +// Ensure that deduction works with lifetime qualifiers. +void deduction(id obj) { + auto a = [[A alloc] init]; + __strong A** aPtr = &a; + + auto a2([[A alloc] init]); + __strong A** aPtr2 = &a2; + + __strong id *idp = new auto(obj); + + __strong id array[17]; + for (auto x : array) { + __strong id *xPtr = &x; + } +} diff --git a/test/SemaObjCXX/arc-bool-conversion.mm b/test/SemaObjCXX/arc-bool-conversion.mm new file mode 100644 index 0000000000..86da3ca90c --- /dev/null +++ b/test/SemaObjCXX/arc-bool-conversion.mm @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify -fblocks -triple x86_64-apple-darwin10.0.0 %s +// rdar://9310049 + +bool fn(id obj) { + return (bool)obj; +} + diff --git a/test/SemaObjCXX/arc-bridged-cast.mm b/test/SemaObjCXX/arc-bridged-cast.mm new file mode 100644 index 0000000000..cbbe79eb27 --- /dev/null +++ b/test/SemaObjCXX/arc-bridged-cast.mm @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -fblocks -verify %s + +typedef const void *CFTypeRef; +typedef const struct __CFString *CFStringRef; + +@interface NSString +@end + +CFTypeRef CFCreateSomething(); +CFStringRef CFCreateString(); +CFTypeRef CFGetSomething(); +CFStringRef CFGetString(); + +id CreateSomething(); +NSString *CreateNSString(); + +template +void from_cf() { + id obj1 = (__bridge_transfer IdType)CFCreateSomething(); + id obj2 = (__bridge_transfer StringType)CFCreateString(); + (__bridge IntPtrType)CFCreateSomething(); // expected-error{{incompatible types casting 'CFTypeRef' (aka 'const void *') to 'int *' with a __bridge cast}} + id obj3 = (__bridge IdType)CFGetSomething(); + id obj4 = (__bridge StringType)CFGetString(); +} + +template void from_cf(); // expected-note{{in instantiation of function template specialization}} + +template +void to_cf(id obj) { + CFTypeRef cf1 = (__bridge_retained IdType)CreateSomething(); + CFStringRef cf2 = (__bridge_retained StringType)CreateNSString(); + CFTypeRef cf3 = (__bridge IdType)CreateSomething(); + CFStringRef cf4 = (__bridge StringType)CreateNSString(); +} + +template void to_cf(id); diff --git a/test/SemaObjCXX/arc-libcxx.mm b/test/SemaObjCXX/arc-libcxx.mm new file mode 100644 index 0000000000..7c3c82eb02 --- /dev/null +++ b/test/SemaObjCXX/arc-libcxx.mm @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -fobjc-arc-cxxlib=libc++ -fobjc-nonfragile-abi -verify %s + +@interface A @end + +void f(__strong id &sir, __weak id &wir, __autoreleasing id &air, + __unsafe_unretained id &uir) { + __strong id *sip = std::addressof(sir); + __weak id *wip = std::addressof(wir); + __autoreleasing id *aip = std::addressof(air); + __unsafe_unretained id *uip = std::addressof(uir); +} diff --git a/test/SemaObjCXX/arc-libstdcxx.mm b/test/SemaObjCXX/arc-libstdcxx.mm new file mode 100644 index 0000000000..a055e36ca0 --- /dev/null +++ b/test/SemaObjCXX/arc-libstdcxx.mm @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -fobjc-arc-cxxlib=libstdc++ -fobjc-nonfragile-abi -verify %s + +@interface A @end + +int check0[std::__is_scalar<__strong id>::__value? -1 : 1]; +int check1[std::__is_scalar<__weak id>::__value? -1 : 1]; +int check2[std::__is_scalar<__autoreleasing id>::__value? -1 : 1]; +int check3[std::__is_scalar<__strong A*>::__value? -1 : 1]; +int check4[std::__is_scalar<__weak A*>::__value? -1 : 1]; +int check5[std::__is_scalar<__autoreleasing A*>::__value? -1 : 1]; diff --git a/test/SemaObjCXX/arc-memfunc.mm b/test/SemaObjCXX/arc-memfunc.mm new file mode 100644 index 0000000000..75b94c64e7 --- /dev/null +++ b/test/SemaObjCXX/arc-memfunc.mm @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify -fblocks %s + +struct X0 { + static id makeObject1() __attribute__((ns_returns_retained)); + id makeObject2() __attribute__((ns_returns_retained)); +}; + +void test_X0(X0 x0, X0 *x0p) { + X0::makeObject1(); + x0.makeObject2(); + x0p->makeObject2(); + id (X0::*pmf)() __attribute__((ns_returns_retained)) = &X0::makeObject2; + (x0.*pmf)(); + (x0p->*pmf)(); +} diff --git a/test/SemaObjCXX/arc-non-pod.mm b/test/SemaObjCXX/arc-non-pod.mm new file mode 100644 index 0000000000..6a7b9109de --- /dev/null +++ b/test/SemaObjCXX/arc-non-pod.mm @@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -Warc-abi -verify -fblocks -triple x86_64-apple-darwin10.0.0 %s + +// Classes that have an Objective-C object pointer. +struct HasObjectMember0 { // expected-warning{{'HasObjectMember0' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}} + id x; +}; + +struct HasObjectMember1 { // expected-warning{{'HasObjectMember1' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}} + id x[3]; +}; + +struct HasObjectMember2 { // expected-warning{{'HasObjectMember2' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}} + id x[3][2]; +}; + +// Don't complain if the type has non-external linkage +namespace { + struct HasObjectMember3 { + id x[3][2]; + }; +} + +// Don't complain if the Objective-C pointer type was explicitly given +// no lifetime. +struct HasObjectMember3 { + __unsafe_unretained id x[3][2]; +}; + +struct HasBlockPointerMember0 { // expected-warning{{'HasBlockPointerMember0' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}} + int (^bp)(int); +}; + +struct HasBlockPointerMember1 { // expected-warning{{'HasBlockPointerMember1' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}} + int (^bp[2][3])(int); +}; + +struct NonPOD { + NonPOD(const NonPOD&); +}; + +struct HasObjectMemberAndNonPOD0 { // expected-warning{{'HasObjectMemberAndNonPOD0' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \ + // expected-warning{{'HasObjectMemberAndNonPOD0' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}} + id x; + NonPOD np; +}; + +struct HasObjectMemberAndNonPOD1 { // expected-warning{{'HasObjectMemberAndNonPOD1' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \ + // expected-warning{{'HasObjectMemberAndNonPOD1' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}} + NonPOD np; + id x[3]; +}; + +struct HasObjectMemberAndNonPOD2 { // expected-warning{{'HasObjectMemberAndNonPOD2' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \ + // expected-warning{{'HasObjectMemberAndNonPOD2' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}} + NonPOD np; + id x[3][2]; +}; + +struct HasObjectMemberAndNonPOD3 { + HasObjectMemberAndNonPOD3 &operator=(const HasObjectMemberAndNonPOD3&); + ~HasObjectMemberAndNonPOD3(); + NonPOD np; + id x[3][2]; +}; + +struct HasBlockPointerMemberAndNonPOD0 { // expected-warning{{'HasBlockPointerMemberAndNonPOD0' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \ +// expected-warning{{'HasBlockPointerMemberAndNonPOD0' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}} + NonPOD np; + int (^bp)(int); +}; + +struct HasBlockPointerMemberAndNonPOD1 { // expected-warning{{'HasBlockPointerMemberAndNonPOD1' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \ +// expected-warning{{'HasBlockPointerMemberAndNonPOD1' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}} + NonPOD np; + int (^bp[2][3])(int); +}; + +int check_non_pod_objc_pointer0[__is_pod(id)? -1 : 1]; +int check_non_pod_objc_pointer1[__is_pod(__strong id)? -1 : 1]; +int check_non_pod_objc_pointer2[__is_pod(__unsafe_unretained id)? 1 : -1]; +int check_non_pod_objc_pointer3[__is_pod(id[2][3])? -1 : 1]; +int check_non_pod_objc_pointer4[__is_pod(__unsafe_unretained id[2][3])? 1 : -1]; +int check_non_pod_block0[__is_pod(int (^)(int))? -1 : 1]; +int check_non_pod_block1[__is_pod(int (^ __unsafe_unretained)(int))? 1 : -1]; + +struct FlexibleArrayMember0 { + int length; + id array[]; // expected-error{{flexible array member 'array' of non-POD element type 'id __strong[]'}} +}; + +struct FlexibleArrayMember1 { + int length; + __unsafe_unretained id array[]; +}; + +// It's okay to pass a retainable type through an ellipsis. +void variadic(...); +void test_variadic() { + variadic(1, 17, @"Foo"); +} + +// It's okay to create a VLA of retainable types. +void vla(int n) { + id vla[n]; +} + +@interface Crufty { + union { + struct { + id object; // expected-note{{has __strong lifetime}} + } an_object; // expected-error{{union member 'an_object' has a non-trivial copy constructor}} + void *ptr; + } storage; +} +@end diff --git a/test/SemaObjCXX/arc-object-init-destroy.mm b/test/SemaObjCXX/arc-object-init-destroy.mm new file mode 100644 index 0000000000..a691f0bc80 --- /dev/null +++ b/test/SemaObjCXX/arc-object-init-destroy.mm @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify -Warc-abi -fblocks -triple x86_64-apple-darwin10.0.0 %s + +typedef __strong id strong_id; +typedef __weak id weak_id; +void test_pseudo_destructors(__strong id *sptr, __weak id *wptr) { + sptr->~id(); // okay + wptr->~id(); // okay + sptr->~strong_id(); // okay + wptr->~weak_id(); + sptr->~weak_id(); // expected-error{{pseudo-destructor destroys object of type '__strong id' with inconsistently-qualified type 'weak_id' (aka '__weak id')}} + wptr->strong_id::~strong_id(); // expected-error{{pseudo-destructor destroys object of type '__weak id' with inconsistently-qualified type 'strong_id' (aka '__strong id')}} + + sptr->id::~id(); // okay + wptr->id::~id(); // okay +} + +void test_delete(__strong id *sptr, __weak id *wptr) { + delete sptr; + delete wptr; + delete [] sptr; // expected-warning{{destroying an array of '__strong id'; this array must not have been allocated from non-ARC code}} + delete [] wptr; // expected-warning{{destroying an array of '__weak id'; this array must not have been allocated from non-ARC code}} +} + +void test_new(int n) { + (void)new strong_id; + (void)new weak_id; + (void)new strong_id [n]; // expected-warning{{allocating an array of 'strong_id' (aka '__strong id'); this array must not be deleted in non-ARC code}} + (void)new weak_id [n]; // expected-warning{{allocating an array of 'weak_id' (aka '__weak id'); this array must not be deleted in non-ARC code}} + + (void)new __strong id; + (void)new __weak id; + (void)new __strong id [n]; // expected-warning{{allocating an array of '__strong id'; this array must not be deleted in non-ARC code}} + + // Infer '__strong'. + __strong id *idptr = new id; + __strong id *idptr2 = new id [n]; // expected-warning{{allocating an array of '__strong id'; this array must not be deleted in non-ARC code}} + + // ... but not for arrays. + typedef id id_array[2][3]; + (void)new id_array; // expected-error{{'new' cannot allocate an array of 'id' with no explicit lifetime}} + + typedef __strong id strong_id_array[2][3]; + typedef __strong id strong_id_3[3]; + strong_id_3 *idptr3 = new strong_id_array; // expected-warning{{allocating an array of '__strong id'; this array must not be deleted in non-ARC code}} +} + +void test_jump_scope() { + goto done; // expected-error{{goto into protected scope}} + __strong id x; // expected-note{{jump bypasses initialization of retaining variable}} + done: + return; +} diff --git a/test/SemaObjCXX/arc-overloading.mm b/test/SemaObjCXX/arc-overloading.mm new file mode 100644 index 0000000000..fb4a0e49f0 --- /dev/null +++ b/test/SemaObjCXX/arc-overloading.mm @@ -0,0 +1,175 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify -fblocks %s + +// Simple lifetime conversions + diagnostics. +int &f0(id __strong const *); // expected-note{{candidate function not viable: 1st argument ('__weak id *') has __weak lifetime, but parameter has __strong lifetime}} + +void test_f0() { + id __strong *sip; + id __strong const *csip; + id __weak *wip; + id __autoreleasing *aip; + id __unsafe_unretained *uip; + + int &ir1 = f0(sip); + int &ir2 = f0(csip); + int &ir3 = f0(aip); + int &ir4 = f0(uip); + f0(wip); // expected-error{{no matching function for call to 'f0'}} +} + +// Simple overloading +int &f1(id __strong const *); +float &f1(id __weak const *); + +void test_f1() { + id __strong *sip; + id __strong const *csip; + id __weak *wip; + id __autoreleasing *aip; + id __unsafe_unretained *uip; + + int &ir1 = f1(sip); + int &ir2 = f1(csip); + float &fr1 = f1(wip); + int &ir3 = f1(aip); + int &ir4 = f1(uip); +} + +// Simple overloading +int &f2(id __strong const *); // expected-note{{candidate function}} +float &f2(id __autoreleasing const *); // expected-note{{candidate function}} + +void test_f2() { + id __strong *sip; + id __strong const *csip; + id __weak *wip; + id __autoreleasing *aip; + id __unsafe_unretained *uip; + + // Prefer non-lifetime conversions to lifetime conversions. + int &ir1 = f2(sip); + int &ir2 = f2(csip); + float &fr1 = f2(aip); + + f2(uip); // expected-error{{call to 'f2' is ambiguous}} +} + +// Writeback conversion +int &f3(id __autoreleasing *); // expected-note{{candidate function not viable: 1st argument ('__unsafe_unretained id *') has __unsafe_unretained lifetime, but parameter has __autoreleasing lifetime}} + +void test_f3() { + id __strong *sip; + id __weak *wip; + id __autoreleasing *aip; + id __unsafe_unretained *uip; + + int &ir1 = f3(sip); + int &ir2 = f3(wip); + int &ir3 = f3(aip); + f3(uip); // expected-error{{no matching function for call to 'f3'}} +} + +// Writeback conversion vs. no conversion +int &f4(id __autoreleasing *); +float &f4(id __strong *); + +void test_f4() { + id __strong *sip; + id __weak *wip; + id __autoreleasing *aip; + extern __weak id *weak_global_ptr; + + float &fr1 = f4(sip); + int &ir1 = f4(wip); + int &ir2 = f4(aip); + int &ir3 = f4(weak_global_ptr); // expected-error{{passing address of non-local object to __autoreleasing parameter for write-back}} +} + +// Writeback conversion vs. other conversion. +int &f5(id __autoreleasing *); +float &f5(id const __unsafe_unretained *); + +void test_f5() { + id __strong *sip; + id __weak *wip; + id __autoreleasing *aip; + + int &ir1 = f5(wip); + float &fr1 = f5(sip); + int &ir2 = f5(aip); +} + +@interface A +@end + +int &f6(id __autoreleasing *); +float &f6(id const __unsafe_unretained *); + +void test_f6() { + A* __strong *sip; + A* __weak *wip; + A* __autoreleasing *aip; + + int &ir1 = f6(wip); + float &fr1 = f6(sip); + int &ir2 = f6(aip); +} + +// Reference binding +void f7(__strong id&); // expected-note{{candidate function not viable: 1st argument ('__weak id') has __weak lifetime, but parameter has __strong lifetime}} \ + // expected-note{{candidate function not viable: 1st argument ('__autoreleasing id') has __autoreleasing lifetime, but parameter has __strong lifetime}} \ + // expected-note{{candidate function not viable: 1st argument ('__unsafe_unretained id') has __unsafe_unretained lifetime, but parameter has __strong lifetime}} + +void test_f7() { + __strong id strong_id; + __weak id weak_id; + __autoreleasing id autoreleasing_id; + __unsafe_unretained id unsafe_id; + f7(strong_id); + f7(weak_id); // expected-error{{no matching function for call to 'f7'}} + f7(autoreleasing_id); // expected-error{{no matching function for call to 'f7'}} + f7(unsafe_id); // expected-error{{no matching function for call to 'f7'}} +} + +void f8(const __strong id&); + +void test_f8() { + __strong id strong_id; + __weak id weak_id; + __autoreleasing id autoreleasing_id; + __unsafe_unretained id unsafe_id; + + f8(strong_id); + f8(weak_id); + f8(autoreleasing_id); + f8(unsafe_id); +} + +int &f9(__strong id&); +float &f9(const __autoreleasing id&); + +void test_f9() { + __strong id strong_id; + __weak id weak_id; + __autoreleasing id autoreleasing_id; + __unsafe_unretained id unsafe_id; + + int &ir1 = f9(strong_id); + float &fr1 = f9(autoreleasing_id); + float &fr2 = f9(unsafe_id); + float &fr2a = f9(weak_id); + + __strong A *strong_a; + __weak A *weak_a; + __autoreleasing A *autoreleasing_a; + __unsafe_unretained A *unsafe_unretained_a; + float &fr3 = f9(strong_a); + float &fr4 = f9(autoreleasing_a); + float &fr5 = f9(unsafe_unretained_a); + float &fr6 = f9(weak_a); + + const __autoreleasing id& ar1 = strong_a; + const __autoreleasing id& ar2 = autoreleasing_a; + const __autoreleasing id& ar3 = unsafe_unretained_a; + const __autoreleasing id& ar4 = weak_a; +} diff --git a/test/SemaObjCXX/arc-system-header.mm b/test/SemaObjCXX/arc-system-header.mm new file mode 100644 index 0000000000..75bed428f6 --- /dev/null +++ b/test/SemaObjCXX/arc-system-header.mm @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fobjc-arc -fobjc-nonfragile-abi -isystem %S/Inputs %s -verify + +#include + +void f(A* a) { + a->data.void_ptr = 0; + a->data.a_b.b = 0; // expected-error{{'a_b' is unavailable: this system field has retaining lifetime}} +} +// Silly location below +// expected-note{{declaration has been explicitly marked unavailable here}} diff --git a/test/SemaObjCXX/arc-templates.mm b/test/SemaObjCXX/arc-templates.mm new file mode 100644 index 0000000000..eeaed89876 --- /dev/null +++ b/test/SemaObjCXX/arc-templates.mm @@ -0,0 +1,219 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify -fblocks %s + +@interface A +@end + +template +struct is_same { + static const bool value = false; +}; + +template +struct is_same { + static const bool value = true; +}; + +// Instantiation for reference/pointer types that will get lifetime +// adjustments. +template +struct X0 { + typedef T* pointer; // expected-error{{pointer to non-const type 'id' with no explicit lifetime}} \ + // expected-error{{pointer to non-const type 'A *' with no explicit lifetime}} + typedef T& reference; // expected-error{{reference to non-const type 'id' with no explicit lifetime}} \ + // expected-error{{reference to non-const type 'A *' with no explicit lifetime}} +}; + +void test_X0() { + X0 x0id; // expected-note{{in instantiation of template class 'X0' requested here}} + X0 x0a; // expected-note{{in instantiation of template class 'X0' requested here}} + X0<__strong A*> x0sa; + + id __strong *ptr; + id __strong val; + X0<__strong id>::pointer &ptr_ref = ptr; + X0<__strong id>::reference ref = val; +} + +// Check template argument deduction (e.g., for specialization) using +// lifetime qualifiers. +template +struct is_pointer_strong { + static const bool value = false; +}; + +template +struct is_pointer_strong<__strong T*> { + static const bool value = true; +}; + +int check_ptr_strong1[is_pointer_strong<__strong id*>::value? 1 : -1]; +int check_ptr_strong2[is_pointer_strong<__weak id*>::value? -1 : 1]; +int check_ptr_strong3[is_pointer_strong<__autoreleasing id*>::value? -1 : 1]; +int check_ptr_strong4[is_pointer_strong<__unsafe_unretained id*>::value? -1 : 1]; +int check_ptr_strong5[is_pointer_strong::value? -1 : 1]; + +// Check substitution into lifetime-qualified dependent types. +template +struct make_strong_pointer { + typedef __strong T *type; +}; + +// Adding qualifiers +int check_make_strong1[is_same::type, __strong id *>::value ? 1 : -1]; +int check_make_strong2[is_same::type, A* __strong *>::value ? 1 : -1]; + +// Adding redundant qualifiers +int check_make_strong3[is_same::type, __strong id *>::value ? 1 : -1]; +int check_make_strong4[is_same::type, A* __strong *>::value ? 1 : -1]; + +// Adding nonsensical qualifiers. +int check_make_strong5[is_same::type, int *>::value ? 1 : -1]; +int check_make_strong6[is_same::type, __weak id *>::value ? 1 : -1]; + +// Check template argument deduction from function templates. +template struct identity { }; + +template identity accept_strong_ptr(__strong T*); +template identity accept_strong_ref(__strong T&); + +template identity accept_any_ptr(T*); +template identity accept_any_ref(T&); + +void test_func_deduction_id() { + __strong id *sip; + __weak id *wip; + __autoreleasing id *aip; + __unsafe_unretained id *uip; + + identity res1 = accept_strong_ptr(sip); + identity<__strong id> res2 = accept_any_ptr(sip); + + __strong id si; + __weak id wi; + __autoreleasing id ai; + __unsafe_unretained id ui; + identity res3 = accept_strong_ref(si); + identity<__strong id> res4 = accept_any_ref(si); + identity<__weak id> res5 = accept_any_ref(wi); + identity<__autoreleasing id> res6 = accept_any_ref(ai); + identity<__unsafe_unretained id> res7 = accept_any_ref(ui); +} + +void test_func_deduction_A() { + __strong A * *sip; + __weak A * *wip; + __autoreleasing A * *aip; + __unsafe_unretained A * *uip; + + identity res1 = accept_strong_ptr(sip); + identity<__strong A *> res2 = accept_any_ptr(sip); + + __strong A * si; + __weak A * wi; + __autoreleasing A * ai; + __unsafe_unretained A * ui; + identity res3 = accept_strong_ref(si); + identity<__strong A *> res4 = accept_any_ref(si); + identity<__weak A *> res5 = accept_any_ref(wi); + identity<__autoreleasing A *> res6 = accept_any_ref(ai); + identity<__unsafe_unretained A *> res7 = accept_any_ref(ui); +} + +// Test partial ordering (qualified vs. non-qualified). +template +struct classify_pointer_pointer { + static const unsigned value = 0; +}; + +template +struct classify_pointer_pointer { + static const unsigned value = 1; +}; + +template +struct classify_pointer_pointer<__strong T*> { + static const unsigned value = 2; +}; + +template +struct classify_pointer_pointer<__weak T*> { + static const unsigned value = 3; +}; + +template +struct classify_pointer_pointer { + static const unsigned value = 4; +}; + +template +struct classify_pointer_pointer<__strong T&> { + static const unsigned value = 5; +}; + +template +struct classify_pointer_pointer<__weak T&> { + static const unsigned value = 6; +}; + +int classify_ptr1[classify_pointer_pointer::value == 0? 1 : -1]; +int classify_ptr2[classify_pointer_pointer::value == 1? 1 : -1]; +int classify_ptr3[classify_pointer_pointer::value == 2? 1 : -1]; +int classify_ptr4[classify_pointer_pointer::value == 3? 1 : -1]; +int classify_ptr5[classify_pointer_pointer::value == 4? 1 : -1]; +int classify_ptr6[classify_pointer_pointer::value == 5? 1 : -1]; +int classify_ptr7[classify_pointer_pointer::value == 6? 1 : -1]; +int classify_ptr8[classify_pointer_pointer::value == 4? 1 : -1]; +int classify_ptr9[classify_pointer_pointer::value == 4? 1 : -1]; +int classify_ptr10[classify_pointer_pointer::value == 1? 1 : -1]; +int classify_ptr11[classify_pointer_pointer::value == 1? 1 : -1]; +int classify_ptr12[classify_pointer_pointer::value == 1? 1 : -1]; +int classify_ptr13[classify_pointer_pointer::value == 2? 1 : -1]; +int classify_ptr14[classify_pointer_pointer::value == 3? 1 : -1]; +int classify_ptr15[classify_pointer_pointer::value == 4? 1 : -1]; +int classify_ptr16[classify_pointer_pointer::value == 5? 1 : -1]; +int classify_ptr17[classify_pointer_pointer::value == 6? 1 : -1]; +int classify_ptr18[classify_pointer_pointer::value == 4? 1 : -1]; +int classify_ptr19[classify_pointer_pointer::value == 4? 1 : -1]; +int classify_ptr20[classify_pointer_pointer::value == 1? 1 : -1]; +int classify_ptr21[classify_pointer_pointer::value == 1? 1 : -1]; + +template int& qual_vs_unqual_ptr(__strong T*); +template double& qual_vs_unqual_ptr(__weak T*); +template float& qual_vs_unqual_ptr(T*); +template int& qual_vs_unqual_ref(__strong T&); +template double& qual_vs_unqual_ref(__weak T&); +template float& qual_vs_unqual_ref(T&); + +void test_qual_vs_unqual_id() { + __strong id *sip; + __weak id *wip; + __autoreleasing id *aip; + __unsafe_unretained id *uip; + + int &ir1 = qual_vs_unqual_ptr(sip); + double &dr1 = qual_vs_unqual_ptr(wip); + float &fr1 = qual_vs_unqual_ptr(aip); + float &fr2 = qual_vs_unqual_ptr(uip); + + int &ir2 = qual_vs_unqual_ref(*sip); + double &dr2 = qual_vs_unqual_ref(*wip); + float &fr3 = qual_vs_unqual_ref(*aip); + float &fr4 = qual_vs_unqual_ref(*uip); +} + +void test_qual_vs_unqual_a() { + __strong A * *sap; + __weak A * *wap; + __autoreleasing A * *aap; + __unsafe_unretained A * *uap; + + int &ir1 = qual_vs_unqual_ptr(sap); + double &dr1 = qual_vs_unqual_ptr(wap); + float &fr1 = qual_vs_unqual_ptr(aap); + float &fr2 = qual_vs_unqual_ptr(uap); + + int &ir2 = qual_vs_unqual_ref(*sap); + double &dr2 = qual_vs_unqual_ref(*wap); + float &fr3 = qual_vs_unqual_ref(*aap); + float &fr4 = qual_vs_unqual_ref(*uap); +} diff --git a/test/SemaObjCXX/arc-type-conversion.mm b/test/SemaObjCXX/arc-type-conversion.mm new file mode 100644 index 0000000000..d1a4121fca --- /dev/null +++ b/test/SemaObjCXX/arc-type-conversion.mm @@ -0,0 +1,198 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify -fblocks %s +// rdar://8843600 + +void * cvt(id arg) // expected-note{{candidate function not viable: cannot convert argument of incomplete type 'void *' to '__strong id'}} +{ + void* voidp_val; + (void)(int*)arg; // expected-error {{cast of an Objective-C pointer to 'int *' is disallowed with ARC}} + (void)(id)arg; + (void)(__autoreleasing id*)arg; // expected-error{{C-style cast from 'id' to '__autoreleasing id *' casts away qualifiers}} + (void)(id*)arg; // expected-error {{pointer to non-const type 'id' with no explicit lifetime}} \ + // expected-error{{C-style cast from 'id' to '__autoreleasing id *' casts away qualifiers}} + + (void)(__autoreleasing id**)voidp_val; + (void)(void*)voidp_val; + (void)(void**)arg; // expected-error {{cast of an Objective-C pointer to 'void **' is disallowed}} + cvt((void*)arg); // expected-error {{cast of Objective-C pointer type 'id' to C pointer type 'void *' requires a bridged cast}} \ + // expected-error {{no matching function for call to 'cvt'}} \ + // expected-note{{use __bridge to convert directly (no change in ownership)}} \ + // expected-note{{use __bridge_retained to make an ARC object available as a +1 'void *'}} + cvt(0); + (void)(__strong id**)(0); + + // FIXME: Diagnostic could be better here. + return arg; // expected-error{{cannot initialize return object of type 'void *' with an lvalue of type '__strong id'}} +} + +// rdar://8898937 +namespace rdar8898937 { + +typedef void (^dispatch_block_t)(void); + +void dispatch_once(dispatch_block_t block); +static void _dispatch_once(dispatch_block_t block) +{ + dispatch_once(block); +} + +} + +void static_casts(id arg) { + void* voidp_val; + (void)static_cast(arg); // expected-error {{cannot cast from type 'id' to pointer type 'int *'}} + (void)static_cast(arg); + (void)static_cast<__autoreleasing id*>(arg); // expected-error{{cannot cast from type 'id' to pointer type '__autoreleasing id *'}} + (void)static_cast(arg); // expected-error {{cannot cast from type 'id' to pointer type '__autoreleasing id *'}} \ + // expected-error{{pointer to non-const type 'id' with no explicit lifetime}} + + (void)static_cast<__autoreleasing id**>(voidp_val); + (void)static_cast(voidp_val); + (void)static_cast(arg); // expected-error {{cannot cast from type 'id' to pointer type 'void **'}} + (void)static_cast<__strong id**>(0); + + __strong id *idp; + (void)static_cast<__autoreleasing id*>(idp); // expected-error{{static_cast from '__strong id *' to '__autoreleasing id *' is not allowed}} + (void)static_cast<__weak id*>(idp); // expected-error{{static_cast from '__strong id *' to '__weak id *' is not allowed}} +} + +void test_const_cast(__strong id *sip, __weak id *wip, + const __strong id *csip, __weak const id *cwip) { + // Cannot use const_cast to cast between lifetime qualifications or + // add/remove lifetime qualifications. + (void)const_cast<__strong id *>(wip); // expected-error{{is not allowed}} + (void)const_cast<__weak id *>(sip); // expected-error{{is not allowed}} + + // It's acceptable to cast away constness. + (void)const_cast<__strong id *>(csip); + (void)const_cast<__weak id *>(cwip); +} + +void test_reinterpret_cast(__strong id *sip, __weak id *wip, + const __strong id *csip, __weak const id *cwip) { + // Okay to reinterpret_cast to add/remove/change lifetime + // qualifications. + (void)reinterpret_cast<__strong id *>(wip); + (void)reinterpret_cast<__weak id *>(sip); + + // Not allowed to cast away constness + (void)reinterpret_cast<__strong id *>(csip); // expected-error{{reinterpret_cast from '__strong id const *' to '__strong id *' casts away qualifiers}} + (void)reinterpret_cast<__weak id *>(cwip); // expected-error{{reinterpret_cast from '__weak id const *' to '__weak id *' casts away qualifiers}} + (void)reinterpret_cast<__weak id *>(csip); // expected-error{{reinterpret_cast from '__strong id const *' to '__weak id *' casts away qualifiers}} + (void)reinterpret_cast<__strong id *>(cwip); // expected-error{{reinterpret_cast from '__weak id const *' to '__strong id *' casts away qualifiers}} +} + +void test_cstyle_cast(__strong id *sip, __weak id *wip, + const __strong id *csip, __weak const id *cwip) { + // C-style casts aren't allowed to change Objective-C lifetime + // qualifiers (beyond what the normal implicit conversion allows). + + (void)(__strong id *)wip; // expected-error{{C-style cast from '__weak id *' to '__strong id *' casts away qualifiers}} + (void)(__strong id *)cwip; // expected-error{{C-style cast from '__weak id const *' to '__strong id *' casts away qualifiers}} + (void)(__weak id *)sip; // expected-error{{C-style cast from '__strong id *' to '__weak id *' casts away qualifiers}} + (void)(__weak id *)csip; // expected-error{{C-style cast from '__strong id const *' to '__weak id *' casts away qualifiers}} + + (void)(__strong const id *)wip; // expected-error{{C-style cast from '__weak id *' to '__strong id const *' casts away qualifiers}} + (void)(__strong const id *)cwip; // expected-error{{C-style cast from '__weak id const *' to '__strong id const *' casts away qualifiers}} + (void)(__weak const id *)sip; // expected-error{{C-style cast from '__strong id *' to '__weak id const *' casts away qualifiers}} + (void)(__weak const id *)csip; // expected-error{{C-style cast from '__strong id const *' to '__weak id const *' casts away qualifiers}} + (void)(__autoreleasing const id *)wip; // expected-error{{C-style cast from '__weak id *' to '__autoreleasing id const *' casts away qualifiers}} + (void)(__autoreleasing const id *)cwip; // expected-error{{C-style cast from '__weak id const *' to '__autoreleasing id const *' casts away qualifiers}} + (void)(__autoreleasing const id *)sip; + (void)(__autoreleasing const id *)csip; +} + +void test_functional_cast(__strong id *sip, __weak id *wip, + __autoreleasing id *aip) { + // Functional casts aren't allowed to change Objective-C lifetime + // qualifiers (beyond what the normal implicit conversion allows). + + typedef __strong id *strong_id_pointer; + typedef __weak id *weak_id_pointer; + typedef __autoreleasing id *autoreleasing_id_pointer; + + typedef const __strong id *const_strong_id_pointer; + typedef const __weak id *const_weak_id_pointer; + typedef const __autoreleasing id *const_autoreleasing_id_pointer; + + (void)strong_id_pointer(wip); // expected-error{{functional-style cast from '__weak id *' to 'strong_id_pointer' (aka '__strong id *') casts away qualifiers}} + (void)weak_id_pointer(sip); // expected-error{{functional-style cast from '__strong id *' to 'weak_id_pointer' (aka '__weak id *') casts away qualifiers}} + (void)autoreleasing_id_pointer(sip); // expected-error{{functional-style cast from '__strong id *' to 'autoreleasing_id_pointer' (aka '__autoreleasing id *') casts away qualifiers}} + (void)autoreleasing_id_pointer(wip); // expected-error{{functional-style cast from '__weak id *' to 'autoreleasing_id_pointer' (aka '__autoreleasing id *') casts away qualifiers}} + (void)const_strong_id_pointer(wip); // expected-error{{functional-style cast from '__weak id *' to 'const_strong_id_pointer' (aka 'const __strong id *') casts away qualifiers}} + (void)const_weak_id_pointer(sip); // expected-error{{functional-style cast from '__strong id *' to 'const_weak_id_pointer' (aka 'const __weak id *') casts away qualifiers}} + (void)const_autoreleasing_id_pointer(sip); + (void)const_autoreleasing_id_pointer(aip); + (void)const_autoreleasing_id_pointer(wip); // expected-error{{functional-style cast from '__weak id *' to 'const_autoreleasing_id_pointer' (aka 'const __autoreleasing id *') casts away qualifiers}} +} + +void test_unsafe_unretained(__strong id *sip, __weak id *wip, + __autoreleasing id *aip, + __unsafe_unretained id *uip, + const __unsafe_unretained id *cuip) { + uip = sip; // expected-error{{assigning to '__unsafe_unretained id *' from incompatible type '__strong id *'}} + uip = wip; // expected-error{{assigning to '__unsafe_unretained id *' from incompatible type '__weak id *'}} + uip = aip; // expected-error{{assigning to '__unsafe_unretained id *' from incompatible type '__autoreleasing id *'}} + + cuip = sip; + cuip = wip; // expected-error{{assigning to '__unsafe_unretained id const *' from incompatible type '__weak id *'}} + cuip = aip; +} + +void to_void(__strong id *sip, __weak id *wip, + __autoreleasing id *aip, + __unsafe_unretained id *uip) { + void *vp1 = sip; + void *vp2 = wip; + void *vp3 = aip; + void *vp4 = uip; + (void)(void*)sip; + (void)(void*)wip; + (void)(void*)aip; + (void)(void*)uip; + (void)static_cast(sip); + (void)static_cast(wip); + (void)static_cast(aip); + (void)static_cast(uip); + (void)reinterpret_cast(sip); + (void)reinterpret_cast(wip); + (void)reinterpret_cast(aip); + (void)reinterpret_cast(uip); + + (void)(void*)&sip; + (void)(void*)&wip; + (void)(void*)&aip; + (void)(void*)&uip; + (void)static_cast(&sip); + (void)static_cast(&wip); + (void)static_cast(&aip); + (void)static_cast(&uip); + (void)reinterpret_cast(&sip); + (void)reinterpret_cast(&wip); + (void)reinterpret_cast(&aip); + (void)reinterpret_cast(&uip); +} + +void from_void(void *vp) { + __strong id *sip = (__strong id *)vp; + __weak id *wip = (__weak id *)vp; + __autoreleasing id *aip = (__autoreleasing id *)vp; + __unsafe_unretained id *uip = (__unsafe_unretained id *)vp; + __strong id *sip2 = static_cast<__strong id *>(vp); + __weak id *wip2 = static_cast<__weak id *>(vp); + __autoreleasing id *aip2 = static_cast<__autoreleasing id *>(vp); + __unsafe_unretained id *uip2 = static_cast<__unsafe_unretained id *>(vp); + __strong id *sip3 = reinterpret_cast<__strong id *>(vp); + __weak id *wip3 = reinterpret_cast<__weak id *>(vp); + __autoreleasing id *aip3 = reinterpret_cast<__autoreleasing id *>(vp); + __unsafe_unretained id *uip3 = reinterpret_cast<__unsafe_unretained id *>(vp); + + __strong id **sipp = (__strong id **)vp; + __weak id **wipp = (__weak id **)vp; + __autoreleasing id **aipp = (__autoreleasing id **)vp; + __unsafe_unretained id **uipp = (__unsafe_unretained id **)vp; + + sip = vp; // expected-error{{assigning to '__strong id *' from incompatible type 'void *'}} + wip = vp; // expected-error{{assigning to '__weak id *' from incompatible type 'void *'}} + aip = vp; // expected-error{{assigning to '__autoreleasing id *' from incompatible type 'void *'}} + uip = vp; // expected-error{{assigning to '__unsafe_unretained id *' from incompatible type 'void *'}} +} diff --git a/test/SemaObjCXX/arc-type-traits.mm b/test/SemaObjCXX/arc-type-traits.mm new file mode 100644 index 0000000000..5f05e337e3 --- /dev/null +++ b/test/SemaObjCXX/arc-type-traits.mm @@ -0,0 +1,90 @@ +// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -fobjc-nonfragile-abi -verify %s + +// Check the results of the various type-trait query functions on +// lifetime-qualified types in ARC. + +#define JOIN3(X,Y) X ## Y +#define JOIN2(X,Y) JOIN3(X,Y) +#define JOIN(X,Y) JOIN2(X,Y) + +#define TRAIT_IS_TRUE(Trait, Type) char JOIN2(Trait,__LINE__)[Trait(Type)? 1 : -1] +#define TRAIT_IS_FALSE(Trait, Type) char JOIN2(Trait,__LINE__)[Trait(Type)? -1 : 1] + +// __has_nothrow_assign +TRAIT_IS_TRUE(__has_nothrow_assign, __strong id); +TRAIT_IS_TRUE(__has_nothrow_assign, __weak id); +TRAIT_IS_TRUE(__has_nothrow_assign, __autoreleasing id); +TRAIT_IS_TRUE(__has_nothrow_assign, __unsafe_unretained id); + +// __has_nothrow_copy +TRAIT_IS_TRUE(__has_nothrow_copy, __strong id); +TRAIT_IS_TRUE(__has_nothrow_copy, __weak id); +TRAIT_IS_TRUE(__has_nothrow_copy, __autoreleasing id); +TRAIT_IS_TRUE(__has_nothrow_copy, __unsafe_unretained id); + +// __has_nothrow_constructor +TRAIT_IS_TRUE(__has_nothrow_constructor, __strong id); +TRAIT_IS_TRUE(__has_nothrow_constructor, __weak id); +TRAIT_IS_TRUE(__has_nothrow_constructor, __autoreleasing id); +TRAIT_IS_TRUE(__has_nothrow_constructor, __unsafe_unretained id); + +// __has_trivial_assign +TRAIT_IS_FALSE(__has_trivial_assign, __strong id); +TRAIT_IS_FALSE(__has_trivial_assign, __weak id); +TRAIT_IS_FALSE(__has_trivial_assign, __autoreleasing id); +TRAIT_IS_TRUE(__has_trivial_assign, __unsafe_unretained id); + +// __has_trivial_copy +TRAIT_IS_FALSE(__has_trivial_copy, __strong id); +TRAIT_IS_FALSE(__has_trivial_copy, __weak id); +TRAIT_IS_FALSE(__has_trivial_copy, __autoreleasing id); +TRAIT_IS_TRUE(__has_trivial_copy, __unsafe_unretained id); + +// __has_trivial_constructor +TRAIT_IS_FALSE(__has_trivial_constructor, __strong id); +TRAIT_IS_FALSE(__has_trivial_constructor, __weak id); +TRAIT_IS_FALSE(__has_trivial_constructor, __autoreleasing id); +TRAIT_IS_TRUE(__has_trivial_constructor, __unsafe_unretained id); + +// __has_trivial_destructor +TRAIT_IS_FALSE(__has_trivial_destructor, __strong id); +TRAIT_IS_FALSE(__has_trivial_destructor, __weak id); +TRAIT_IS_TRUE(__has_trivial_destructor, __autoreleasing id); +TRAIT_IS_TRUE(__has_trivial_destructor, __unsafe_unretained id); + +// __is_literal +TRAIT_IS_FALSE(__is_literal, __strong id); +TRAIT_IS_FALSE(__is_literal, __weak id); +TRAIT_IS_FALSE(__is_literal, __autoreleasing id); +TRAIT_IS_FALSE(__is_literal, __unsafe_unretained id); + +// __is_literal_type +TRAIT_IS_FALSE(__is_literal_type, __strong id); +TRAIT_IS_FALSE(__is_literal_type, __weak id); +TRAIT_IS_FALSE(__is_literal_type, __autoreleasing id); +TRAIT_IS_FALSE(__is_literal_type, __unsafe_unretained id); + +// __is_pod +TRAIT_IS_FALSE(__is_pod, __strong id); +TRAIT_IS_FALSE(__is_pod, __weak id); +TRAIT_IS_FALSE(__is_pod, __autoreleasing id); +TRAIT_IS_TRUE(__is_pod, __unsafe_unretained id); + +// __is_trivial +TRAIT_IS_FALSE(__is_trivial, __strong id); +TRAIT_IS_FALSE(__is_trivial, __weak id); +TRAIT_IS_FALSE(__is_trivial, __autoreleasing id); +TRAIT_IS_TRUE(__is_trivial, __unsafe_unretained id); + +// __is_scalar +TRAIT_IS_FALSE(__is_scalar, __strong id); +TRAIT_IS_FALSE(__is_scalar, __weak id); +TRAIT_IS_FALSE(__is_scalar, __autoreleasing id); +TRAIT_IS_TRUE(__is_scalar, __unsafe_unretained id); + +// __is_standard_layout +TRAIT_IS_TRUE(__is_standard_layout, __strong id); +TRAIT_IS_TRUE(__is_standard_layout, __weak id); +TRAIT_IS_TRUE(__is_standard_layout, __autoreleasing id); +TRAIT_IS_TRUE(__is_standard_layout, __unsafe_unretained id); + diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 2a9d96db4c..f9cd40820d 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -2374,7 +2374,8 @@ CXTranslationUnit clang_createTranslationUnit(CXIndex CIdx, unsigned clang_defaultEditingTranslationUnitOptions() { return CXTranslationUnit_PrecompiledPreamble | CXTranslationUnit_CacheCompletionResults | - CXTranslationUnit_CXXPrecompiledPreamble; + CXTranslationUnit_CXXPrecompiledPreamble | + CXTranslationUnit_CXXChainedPCH; } CXTranslationUnit @@ -4797,6 +4798,7 @@ static void clang_annotateTokensImpl(void *UserData) { llvm::StringSwitch(II->getName()) .Case("readonly", true) .Case("assign", true) + .Case("unsafe_unretained", true) .Case("readwrite", true) .Case("retain", true) .Case("copy", true) @@ -4804,6 +4806,8 @@ static void clang_annotateTokensImpl(void *UserData) { .Case("atomic", true) .Case("getter", true) .Case("setter", true) + .Case("strong", true) + .Case("weak", true) .Default(false)) Tokens[I].int_data[0] = CXToken_Keyword; } diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp index b34370d2e6..3513451341 100644 --- a/tools/libclang/CXCursor.cpp +++ b/tools/libclang/CXCursor.cpp @@ -92,6 +92,7 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, case Stmt::ObjCAtFinallyStmtClass: case Stmt::ObjCAtThrowStmtClass: case Stmt::ObjCAtSynchronizedStmtClass: + case Stmt::ObjCAutoreleasePoolStmtClass: case Stmt::ObjCForCollectionStmtClass: case Stmt::CXXCatchStmtClass: case Stmt::CXXTryStmtClass: @@ -167,7 +168,9 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, case Stmt::ObjCEncodeExprClass: case Stmt::ObjCSelectorExprClass: case Stmt::ObjCProtocolExprClass: - case Stmt::ObjCIsaExprClass: + case Stmt::ObjCIsaExprClass: + case Stmt::ObjCIndirectCopyRestoreExprClass: + case Stmt::ObjCBridgedCastExprClass: case Stmt::ShuffleVectorExprClass: case Stmt::BlockExprClass: case Stmt::OpaqueValueExprClass: diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp index 5af955357b..45c73468d9 100644 --- a/tools/libclang/CXType.cpp +++ b/tools/libclang/CXType.cpp @@ -366,7 +366,11 @@ unsigned clang_isPODType(CXType X) { QualType T = GetQualType(X); if (!T.getTypePtrOrNull()) return 0; - return T->isPODType() ? 1 : 0; + + CXTranslationUnit TU = GetTU(X); + ASTUnit *AU = static_cast(TU->TUData); + + return T.isPODType(AU->getASTContext()) ? 1 : 0; } CXString clang_getDeclObjCTypeEncoding(CXCursor C) { -- 2.50.1