Allow (Object *)kMyGlobalCFObj casts without bridging

Previously we allowed these casts only for constants declared in system
headers, which we assume are retain/release-neutral. Now also allow them
for constants in user headers, treating them as +0.  Practically, this
means that we will now allow:
id x = (id)kMyGlobalConst;

But unlike with system headers we cannot mix them with +1 values:
id y = (id)(b ? kMyGlobalConst : [Obj newValAtPlusOne]); // error
id z = (id)(b ? kSystemGlobalConst: [Obj newValAtPlusOne]); // OK

Thanks to John for suggesting this improvement.

llvm-svn: 230534
This commit is contained in:
Ben Langmuir 2015-02-25 20:09:06 +00:00
parent dcc84db264
commit 443aa4b4b0
6 changed files with 44 additions and 23 deletions

View File

@ -594,7 +594,9 @@ retainable pointer type <arc.misc.c-retainable>` and it is:
* 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:
@ -631,6 +633,12 @@ retain-agnostic, the conversion is treated as a ``__bridge`` cast.
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

View File

@ -3031,17 +3031,20 @@ namespace {
/// 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.

View File

@ -7,6 +7,7 @@
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;
@ -28,11 +29,15 @@ struct StrS {
@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];

View File

@ -7,6 +7,7 @@
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;
@ -28,11 +29,15 @@ struct StrS {
@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));

View File

@ -28,19 +28,12 @@ id CFBridgingRelease(CFTypeRef __attribute__((cf_consumed)) X);
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}:")"

View File

@ -32,15 +32,20 @@ CFStringRef auditedString(void);
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}}
@ -68,11 +73,13 @@ void test1(int cond) {
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