[Biabduction] [Objective-C] Retain Cycle not work when wrapping cycles.
Please include the following information:
- [x] Infer version: v1.1.0.
- [x] Your operating system: "MacOS-12.1".
- [x] The command I ran:
infer run --biabduction --debug -- clang -fobjc-arc -framework Foundation main.m -o main - [x] The full output see below.
- [x] A minimal example to reproduce your problem, see below.
1. Only Objective-C code
@JumpMasterJJ and I try to use infer biabduction to analyze Objective-C "Retain Cycle". This is our code:
#import <Foundation/Foundation.h>
@class Child;
@class Parent;
struct ParentWrapper {
Parent *parent;
};
struct ChildWrapper {
Child *child;
};
@interface Parent : NSObject {
@public struct ChildWrapper childWrapper;
}
-(void) dealloc;
@end
@interface Child : NSObject {
@public struct ParentWrapper parentWrapper;
}
-(void) dealloc;
@end
@implementation Parent
-(void) dealloc {
NSLog(@"this is parent dealloc function");
}
@end
@implementation Child
-(void) dealloc {
NSLog(@"this is parent dealloc function");
}
@end
int foo(int argc, const char* argv[]) {
Parent *parent = [[Parent alloc] init];
Child *child = [[Child alloc] init];
parent->childWrapper.child = child;
child->parentWrapper.parent = parent;
return 0;
}
int main(int argc, const char* argv[]) {
foo(argc, argv);
return 0;
}
You can easily know there is a reference counting problem in the above code. And I can confirm it since I also compiled it without infer. It didn't print log due to retain cycles:
clang -fobjc-arc -framework Foundation main.m -o main
./main
# No output
If I remove child->parentWrapper.parent = parent;, it can print logs successfully:
#import <Foundation/Foundation.h>
@class Child;
@class Parent;
struct ParentWrapper {
Parent *parent;
};
struct ChildWrapper {
Child *child;
};
@interface Parent : NSObject {
@public struct ChildWrapper childWrapper;
}
-(void) dealloc;
@end
@interface Child : NSObject {
@public struct ParentWrapper parentWrapper;
}
-(void) dealloc;
@end
@implementation Parent
-(void) dealloc {
NSLog(@"this is parent dealloc function");
}
@end
@implementation Child
-(void) dealloc {
NSLog(@"this is parent dealloc function");
}
@end
int foo(int argc, const char* argv[]) {
Parent *parent = [[Parent alloc] init];
Child *child = [[Child alloc] init];
parent->childWrapper.child = child;
// child->parentWrapper.parent = parent;
return 0;
}
int main(int argc, const char* argv[]) {
foo(argc, argv);
return 0;
}
It can print:
./main
2023-05-17 19:32:49.180 main[25913:168343] this is parent dealloc function
2023-05-17 19:32:49.180 main[25913:168343] this is parent dealloc function
2. With infer biabduction
We ran for the first piece of code:
infer run --biabduction --debug -- clang -fobjc-arc -framework Foundation main.m -o main
But I got:
1/1 [################################################################################] 100% 514ms
No issues found
3. Potential problem
We also try to write some other pieces of Objective-C program, like this:
#import <Foundation/Foundation.h>
@class Child;
@interface Parent : NSObject {
@public Child *child;
}
@end
@interface Child : NSObject {
@public Parent *parent;
}
@end
@implementation Parent
@end
@implementation Child
@end
int foo(int argc, const char* argv[]) {
Parent *parent = [[Parent alloc] init];
Child *child = [[Child alloc] init];
parent->child = child;
child->parent = parent;
return 0;
}
int main(int argc, const char* argv[]) {
return 0;
}
Apparently, it also has the same kind of problem. And we ran infer:
infer run --biabduction --debug -- clang -fobjc-arc -framework Foundation main-simple.m -o main-simple
1/1 [################################################################################] 100% 800ms
main-simple.m:25: error: Biabduction Retain Cycle
Retain cycle at line 25, column 5 involving the following objects:
(1) `child` of type `Child*` --> `parent`, last assigned on line 25
(2) `parent` of type `Parent*` --> `child`, last assigned on line 24.
23.
24. parent->child = child;
25. child->parent = parent;
^
26. return 0;
27. }
Found 1 issue
Issue Type(ISSUED_TYPE_ID): #
Biabduction Retain Cycle(BIABDUCTION_RETAIN_CYCLE): 1
So we think that it's related to "wrapper", which means that current Retain Cycle checker might be not able to handle nested data structure.