diff options
-rw-r--r-- | docs/AutomaticReferenceCounting.html | 9 | ||||
-rw-r--r-- | lib/CodeGen/CGObjC.cpp | 11 | ||||
-rw-r--r-- | lib/Sema/SemaObjCProperty.cpp | 19 | ||||
-rw-r--r-- | test/CodeGenObjC/arc-property.m | 72 |
4 files changed, 109 insertions, 2 deletions
diff --git a/docs/AutomaticReferenceCounting.html b/docs/AutomaticReferenceCounting.html index 3f1ccaf672..2fd82dda65 100644 --- a/docs/AutomaticReferenceCounting.html +++ b/docs/AutomaticReferenceCounting.html @@ -888,6 +888,15 @@ from non-ARC practice was acceptable because we had conservatively banned the synthesis in order to give ourselves exactly this leeway.</p></div> +<p>Applying <tt>__attribute__((NSObject))</tt> to a property not of +retainable object pointer type has the same behavior it does outside +of ARC: it requires the property type to be some sort of pointer and +permits the use of modifiers other than <tt>assign</tt>. These +modifiers only affect the synthesized getter and setter; direct +accesses to the ivar (even if synthesized) still have primitive +semantics, and the value in the ivar will not be automatically +released during deallocation.</p> + </div> <!-- ownership.spelling.property --> </div> <!-- ownership.spelling --> diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index 4ac172d1cb..e0762846dc 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -613,7 +613,16 @@ PropertyImplStrategy::PropertyImplStrategy(CodeGenModule &CGM, // which translates to objc_storeStrong. This isn't required, but // it's slightly nicer. } else if (CGM.getLangOpts().ObjCAutoRefCount && !IsAtomic) { - Kind = Expression; + // Using standard expression emission for the setter is only + // acceptable if the ivar is __strong, which won't be true if + // the property is annotated with __attribute__((NSObject)). + // TODO: falling all the way back to objc_setProperty here is + // just laziness, though; we could still use objc_storeStrong + // if we hacked it right. + if (ivarType.getObjCLifetime() == Qualifiers::OCL_Strong) + Kind = Expression; + else + Kind = SetPropertyAndExpressionGet; return; // Otherwise, we need to at least use setProperty. However, if diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index 27deab226f..8a8927d362 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -43,7 +43,7 @@ static Qualifiers::ObjCLifetime getImpliedARCOwnership( if (attrs & (ObjCPropertyDecl::OBJC_PR_retain | ObjCPropertyDecl::OBJC_PR_strong | ObjCPropertyDecl::OBJC_PR_copy)) { - return type->getObjCARCImplicitLifetime(); + return Qualifiers::OCL_Strong; } else if (attrs & ObjCPropertyDecl::OBJC_PR_weak) { return Qualifiers::OCL_Weak; } else if (attrs & ObjCPropertyDecl::OBJC_PR_unsafe_unretained) { @@ -543,6 +543,23 @@ static void checkARCPropertyImpl(Sema &S, SourceLocation propertyImplLoc, ivarLifetime == Qualifiers::OCL_Autoreleasing) return; + // If the ivar is private, and it's implicitly __unsafe_unretained + // becaues of its type, then pretend it was actually implicitly + // __strong. This is only sound because we're processing the + // property implementation before parsing any method bodies. + if (ivarLifetime == Qualifiers::OCL_ExplicitNone && + propertyLifetime == Qualifiers::OCL_Strong && + ivar->getAccessControl() == ObjCIvarDecl::Private) { + SplitQualType split = ivarType.split(); + if (split.Quals.hasObjCLifetime()) { + assert(ivarType->isObjCARCImplicitlyUnretainedType()); + split.Quals.setObjCLifetime(Qualifiers::OCL_Strong); + ivarType = S.Context.getQualifiedType(split); + ivar->setType(ivarType); + return; + } + } + switch (propertyLifetime) { case Qualifiers::OCL_Strong: S.Diag(propertyImplLoc, diag::err_arc_strong_property_ownership) diff --git a/test/CodeGenObjC/arc-property.m b/test/CodeGenObjC/arc-property.m index 6c5180b1c3..68344b83b0 100644 --- a/test/CodeGenObjC/arc-property.m +++ b/test/CodeGenObjC/arc-property.m @@ -13,3 +13,75 @@ void test0(Test0 *t0, id value) { // CHECK: @objc_msgSend // CHECK: call void @objc_release( // CHECK: call void @objc_release( + +struct S1 { Class isa; }; +@interface Test1 +@property (nonatomic, strong) __attribute__((NSObject)) struct S1 *pointer; +@end +@implementation Test1 +@synthesize pointer; +@end +// The getter should be a simple load. +// CHECK: define internal [[S1:%.*]]* @"\01-[Test1 pointer]"( +// CHECK: [[OFFSET:%.*]] = load i64* @"OBJC_IVAR_$_Test1.pointer" +// CHECK-NEXT: [[T0:%.*]] = bitcast [[TEST1:%.*]]* {{%.*}} to i8* +// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8* [[T0]], i64 [[OFFSET]] +// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to [[S1]]** +// CHECK-NEXT: [[T3:%.*]] = load [[S1]]** [[T2]], align 8 +// CHECK-NEXT: ret [[S1]]* [[T3]] + +// The setter should be using objc_setProperty. +// CHECK: define internal void @"\01-[Test1 setPointer:]"( +// CHECK: [[T0:%.*]] = bitcast [[TEST1]]* {{%.*}} to i8* +// CHECK-NEXT: [[OFFSET:%.*]] = load i64* @"OBJC_IVAR_$_Test1.pointer" +// CHECK-NEXT: [[T1:%.*]] = load [[S1]]** {{%.*}} +// CHECK-NEXT: [[T2:%.*]] = bitcast [[S1]]* [[T1]] to i8* +// CHECK-NEXT: call void @objc_setProperty(i8* [[T0]], i8* {{%.*}}, i64 [[OFFSET]], i8* [[T2]], i1 zeroext false, i1 zeroext false) +// CHECK-NEXT: ret void + + +// rdar://problem/12039404 +@interface Test2 { +@private + Class _theClass; +} +@property (copy) Class theClass; +@end + +static Class theGlobalClass; +@implementation Test2 +@synthesize theClass = _theClass; +- (void) test { + _theClass = theGlobalClass; +} +@end +// CHECK: define internal void @"\01-[Test2 test]"( +// CHECK: [[T0:%.*]] = load i8** @theGlobalClass, align 8 +// CHECK-NEXT: [[T1:%.*]] = load [[TEST2:%.*]]** +// CHECK-NEXT: [[OFFSET:%.*]] = load i64* @"OBJC_IVAR_$_Test2._theClass" +// CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST2]]* [[T1]] to i8* +// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds i8* [[T2]], i64 [[OFFSET]] +// CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to i8** +// CHECK-NEXT: call void @objc_storeStrong(i8** [[T4]], i8* [[T0]]) nounwind +// CHECK-NEXT: ret void + +// CHECK: define internal i8* @"\01-[Test2 theClass]"( +// CHECK: [[OFFSET:%.*]] = load i64* @"OBJC_IVAR_$_Test2._theClass" +// CHECK-NEXT: [[T0:%.*]] = call i8* @objc_getProperty(i8* {{.*}}, i8* {{.*}}, i64 [[OFFSET]], i1 zeroext true) +// CHECK-NEXT: ret i8* [[T0]] + +// CHECK: define internal void @"\01-[Test2 setTheClass:]"( +// CHECK: [[T0:%.*]] = bitcast [[TEST2]]* {{%.*}} to i8* +// CHECK-NEXT: [[OFFSET:%.*]] = load i64* @"OBJC_IVAR_$_Test2._theClass" +// CHECK-NEXT: [[T1:%.*]] = load i8** {{%.*}} +// CHECK-NEXT: call void @objc_setProperty(i8* [[T0]], i8* {{%.*}}, i64 [[OFFSET]], i8* [[T1]], i1 zeroext true, i1 zeroext true) +// CHECK-NEXT: ret void + +// CHECK: define internal void @"\01-[Test2 .cxx_destruct]"( +// CHECK: [[T0:%.*]] = load [[TEST2]]** +// CHECK-NEXT: [[OFFSET:%.*]] = load i64* @"OBJC_IVAR_$_Test2._theClass" +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST2]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds i8* [[T1]], i64 [[OFFSET]] +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to i8** +// CHECK-NEXT: call void @objc_storeStrong(i8** [[T3]], i8* null) nounwind +// CHECK-NEXT: ret void |