* a message send, and the declared method either has the
``cf_returns_not_retained`` attribute or it has neither the
``cf_returns_retained`` attribute nor a :ref:`selector family
- <arc.method-families>` that implies a retained result.
+ <arc.method-families>` that implies a retained result, or
+* :when-revised:`[beginning LLVM 3.6]` :revision:`a load from a` ``const``
+ :revision:`non-system global variable.`
An expression is :arc-term:`known retained` if it is an rvalue of :ref:`C
retainable pointer type <arc.misc.c-retainable>` and it is:
to an ObjC-typed local, and then calling ``CFRelease`` when done --- are a
bit too likely to be accidentally accepted, leading to mysterious behavior.
+ For loads from ``const`` global variables of :ref:`C retainable pointer type
+ <arc.misc.c-retainable>`, it is reasonable to assume that global system
+ constants were initialitzed with true constants (e.g. string literals), but
+ user constants might have been initialized with something dynamically
+ allocated, using a global initializer.
+
.. _arc.objects.restrictions.conversion-exception-contextual:
Conversion from retainable object pointer type in certain contexts
/// Some declaration references are okay.
ACCResult VisitDeclRefExpr(DeclRefExpr *e) {
- // References to global constants from system headers are okay.
- // These are things like 'kCFStringTransformToLatin'. They are
- // can also be assumed to be immune to retains.
VarDecl *var = dyn_cast<VarDecl>(e->getDecl());
+ // References to global constants are okay.
if (isAnyRetainable(TargetClass) &&
isAnyRetainable(SourceClass) &&
var &&
var->getStorageClass() == SC_Extern &&
- var->getType().isConstQualified() &&
- Context.getSourceManager().isInSystemHeader(var->getLocation())) {
- return ACC_bottom;
+ var->getType().isConstQualified()) {
+
+ // In system headers, they can also be assumed to be immune to retains.
+ // These are things like 'kCFStringTransformToLatin'.
+ if (Context.getSourceManager().isInSystemHeader(var->getLocation()))
+ return ACC_bottom;
+
+ return ACC_plusZero;
}
// Nothing else.
typedef const struct __CFString * CFStringRef;
extern const CFStringRef kUTTypePlainText;
extern const CFStringRef kUTTypeRTF;
+extern CFStringRef kNonConst;
typedef const struct __CFAllocator * CFAllocatorRef;
typedef const struct __CFUUID * CFUUIDRef;
@end
void f(BOOL b, id p) {
- NSString *str = (NSString *)kUTTypePlainText;
- str = b ? kUTTypeRTF : kUTTypePlainText;
- str = (NSString *)(b ? kUTTypeRTF : kUTTypePlainText);
+ NSString *str = (NSString *)kUTTypePlainText; // no change
+ str = b ? kUTTypeRTF : kUTTypePlainText; // no change
+ str = (NSString *)(b ? kUTTypeRTF : kUTTypePlainText); // no change
str = (NSString *)p; // no change.
+ str = (NSString *)kNonConst;
+ str = b ? kUTTypeRTF : kNonConst;
+ str = (NSString *)(b ? kUTTypeRTF : kNonConst);
+
CFUUIDRef _uuid;
NSString *_uuidString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, _uuid);
_uuidString = [(NSString *)CFUUIDCreateString(kCFAllocatorDefault, _uuid) autorelease];
typedef const struct __CFString * CFStringRef;
extern const CFStringRef kUTTypePlainText;
extern const CFStringRef kUTTypeRTF;
+extern CFStringRef kNonConst;
typedef const struct __CFAllocator * CFAllocatorRef;
typedef const struct __CFUUID * CFUUIDRef;
@end
void f(BOOL b, id p) {
- NSString *str = (__bridge NSString *)kUTTypePlainText;
- str = (__bridge NSString *)(b ? kUTTypeRTF : kUTTypePlainText);
- str = (__bridge NSString *)(b ? kUTTypeRTF : kUTTypePlainText);
+ NSString *str = (NSString *)kUTTypePlainText; // no change
+ str = b ? kUTTypeRTF : kUTTypePlainText; // no change
+ str = (NSString *)(b ? kUTTypeRTF : kUTTypePlainText); // no change
str = (NSString *)p; // no change.
+ str = (__bridge NSString *)kNonConst;
+ str = (__bridge NSString *)(b ? kUTTypeRTF : kNonConst);
+ str = (__bridge NSString *)(b ? kUTTypeRTF : kNonConst);
+
CFUUIDRef _uuid;
NSString *_uuidString = (NSString *)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, _uuid));
_uuidString = (NSString *)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, _uuid));
NSMutableString *test() {
NSDictionary *infoDictionary;
- infoDictionary[kCFBundleNameKey] = 0; // expected-error {{indexing expression is invalid because subscript type 'CFStringRef' (aka 'const struct __CFString *') is not an integral or Objective-C pointer type}} \
- // expected-error {{implicit conversion of C pointer type 'CFStringRef' (aka 'const struct __CFString *') to Objective-C pointer type '__strong id<NSCopying>' requires a bridged cast}} \
- // expected-note {{use __bridge to convert directly (no change in ownership)}} \
- // expected-note {{use CFBridgingRelease call to transfer ownership of a +1 'CFStringRef' (aka 'const struct __CFString *') into ARC}}
+ infoDictionary[kCFBundleNameKey] = 0; // expected-error {{indexing expression is invalid because subscript type 'CFStringRef' (aka 'const struct __CFString *') is not an integral or Objective-C pointer type}}
return infoDictionary[CFStringCreateMutable(((void*)0), 100)]; // expected-error {{indexing expression is invalid because subscript type 'CFMutableStringRef' (aka 'struct __CFString *') is not an integral or Objective-C pointer type}} \
// expected-error {{implicit conversion of C pointer type 'CFMutableStringRef' (aka 'struct __CFString *') to Objective-C pointer type '__strong id<NSCopying>' requires a bridged cast}} \
// expected-note {{use CFBridgingRelease call to transfer ownership of a +1 'CFMutableStringRef' (aka 'struct __CFString *') into ARC}}
}
-// CHECK: fix-it:"{{.*}}":{31:18-31:18}:"(__bridge __strong id<NSCopying>)("
-// CHECK: fix-it:"{{.*}}":{31:34-31:34}:")"
-// CHECK: fix-it:"{{.*}}":{31:18-31:18}:"CFBridgingRelease("
-// CHECK: fix-it:"{{.*}}":{31:34-31:34}:")"
-// CHECK: fix-it:"{{.*}}":{35:25-35:25}:"CFBridgingRelease("
-// CHECK: fix-it:"{{.*}}":{35:63-35:63}:")"
+// CHECK: fix-it:"{{.*}}":{32:25-32:25}:"CFBridgingRelease("
+// CHECK: fix-it:"{{.*}}":{32:63-32:63}:")"
CFStringRef auditedCreateString(void);
#pragma clang arc_cf_code_audited end
+extern const CFStringRef kUserConst;
+
void test1(int cond) {
id x;
x = (id) auditedString();
+ x = (id) kUserConst;
x = (id) (cond ? auditedString() : (void*) 0);
x = (id) (cond ? (void*) 0 : auditedString());
x = (id) (cond ? (CFStringRef) @"help" : auditedString());
+ x = (id) (cond ? (CFStringRef) @"help" : kUserConst);
x = (id) unauditedString(); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use CFBridgingRelease call to}}
x = (id) (cond ? unauditedString() : (void*) 0); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use CFBridgingRelease call to}}
+ x = (id) (cond ? unauditedString() : kUserConst); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use CFBridgingRelease call to}}
x = (id) (cond ? (void*) 0 : unauditedString()); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use CFBridgingRelease call to}}
x = (id) (cond ? (CFStringRef) @"help" : unauditedString()); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use CFBridgingRelease call to}}
x = (id) (cond ? [object makeString] : (void*) 0);
x = (id) (cond ? (void*) 0 : [object makeString]);
x = (id) (cond ? (CFStringRef) @"help" : [object makeString]);
+ x = (id) (cond ? kUserConst : [object makeString]);
x = (id) [object newString];
x = (id) (cond ? [object newString] : (void*) 0);
x = (id) (cond ? (void*) 0 : [object newString]);
x = (id) (cond ? (CFStringRef) @"help" : [object newString]); // a bit questionable
+ x = (id) (cond ? kUserConst : [object newString]); // expected-error{{requires a bridged cast}} expected-note{{use __bridge to}} expected-note{{use CFBridgingRelease call to}}
}
// rdar://problem/10246264