Because the name of a direct method must be agreed upon by the caller and the implementation, certain bad practices that one can get away with when using dynamism are fatal with direct methods. To avoid really weird and unscruttable linker error, tighten the front-end error reporting. Rule 1: Direct methods can only have at most one declaration in an @interface container. Any redeclaration is strictly forbidden. Today some amount of redeclaration is tolerated between the main interface and categories for dynamic methods, but we can't have that. Rule 2: Direct method implementations can only be declared in a matching @interface container: when implemented in the primary @implementation then the declaration must be in the primary @interface or an extension, and when implemented in a category, the declaration must be in the @interface for the same category. Also fix another issue with ObjCMethod::getCanonicalDecl(): when an implementation lives in the primary @interface, then its canonical declaration can be in any extension, even when it's not an accessor. Add Sema tests to cover the new errors, and CG tests to beef up testing around function names for categories and extensions. Radar-Id: <rdar://problem/58054563> Differential Revision: https://reviews.llvm.org/D71694
238 lines
7.6 KiB
Objective-C
238 lines
7.6 KiB
Objective-C
// RUN: %clang_cc1 -emit-llvm -fobjc-arc -triple x86_64-apple-darwin10 %s -o - | FileCheck %s
|
|
|
|
struct my_complex_struct {
|
|
int a, b;
|
|
};
|
|
|
|
struct my_aggregate_struct {
|
|
int a, b;
|
|
char buf[128];
|
|
};
|
|
|
|
__attribute__((objc_root_class))
|
|
@interface Root
|
|
- (int)getInt __attribute__((objc_direct));
|
|
@property(direct, readonly) int intProperty;
|
|
@property(direct, readonly) int intProperty2;
|
|
@end
|
|
|
|
@implementation Root
|
|
// CHECK-LABEL: define hidden i32 @"\01-[Root intProperty2]"
|
|
- (int)intProperty2 {
|
|
return 42;
|
|
}
|
|
|
|
// CHECK-LABEL: define hidden i32 @"\01-[Root getInt]"(
|
|
- (int)getInt __attribute__((objc_direct)) {
|
|
// loading parameters
|
|
// CHECK-LABEL: entry:
|
|
// CHECK-NEXT: [[RETVAL:%.*]] = alloca
|
|
// CHECK-NEXT: [[SELFADDR:%.*]] = alloca %0*,
|
|
// CHECK-NEXT: [[_CMDADDR:%.*]] = alloca i8*,
|
|
// CHECK-NEXT: store %0* %{{.*}}, %0** [[SELFADDR]],
|
|
// CHECK-NEXT: store i8* %{{.*}}, i8** [[_CMDADDR]],
|
|
|
|
// self nil-check
|
|
// CHECK-NEXT: [[SELF:%.*]] = load %0*, %0** [[SELFADDR]],
|
|
// CHECK-NEXT: [[NILCHECK:%.*]] = icmp eq %0* [[SELF]], null
|
|
// CHECK-NEXT: br i1 [[NILCHECK]],
|
|
|
|
// setting return value to nil
|
|
// CHECK-LABEL: objc_direct_method.self_is_nil:
|
|
// CHECK: [[RET0:%.*]] = bitcast{{.*}}[[RETVAL]]
|
|
// CHECK-NEXT: call void @llvm.memset{{[^(]*}}({{[^,]*}}[[RET0]], i8 0,
|
|
// CHECK-NEXT: br label
|
|
|
|
// set value
|
|
// CHECK-LABEL: objc_direct_method.cont:
|
|
// CHECK: store{{.*}}[[RETVAL]],
|
|
// CHECK-NEXT: br label
|
|
|
|
// return
|
|
// CHECK-LABEL: return:
|
|
// CHECK: {{%.*}} = load{{.*}}[[RETVAL]],
|
|
// CHECK-NEXT: ret
|
|
return 42;
|
|
}
|
|
|
|
// CHECK-LABEL: define hidden i32 @"\01+[Root classGetInt]"(
|
|
+ (int)classGetInt __attribute__((objc_direct)) {
|
|
// loading parameters
|
|
// CHECK-LABEL: entry:
|
|
// CHECK-NEXT: [[SELFADDR:%.*]] = alloca i8*,
|
|
// CHECK-NEXT: [[_CMDADDR:%.*]] = alloca i8*,
|
|
// CHECK-NEXT: store i8* %{{.*}}, i8** [[SELFADDR]],
|
|
// CHECK-NEXT: store i8* %{{.*}}, i8** [[_CMDADDR]],
|
|
|
|
// [self self]
|
|
// CHECK-NEXT: [[SELF:%.*]] = load i8*, i8** [[SELFADDR]],
|
|
// CHECK-NEXT: [[SELFSEL:%.*]] = load i8*, i8** @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: [[SELF0:%.*]] = call {{.*}} @objc_msgSend
|
|
// CHECK-NEXT: store i8* [[SELF0]], i8** [[SELFADDR]],
|
|
|
|
// return
|
|
// CHECK-NEXT: ret
|
|
return 42;
|
|
}
|
|
|
|
// CHECK-LABEL: define hidden i64 @"\01-[Root getComplex]"(
|
|
- (struct my_complex_struct)getComplex __attribute__((objc_direct)) {
|
|
// loading parameters
|
|
// CHECK-LABEL: entry:
|
|
// CHECK-NEXT: [[RETVAL:%.*]] = alloca
|
|
// CHECK-NEXT: [[SELFADDR:%.*]] = alloca %0*,
|
|
// CHECK-NEXT: [[_CMDADDR:%.*]] = alloca i8*,
|
|
// CHECK-NEXT: store %0* %{{.*}}, %0** [[SELFADDR]],
|
|
// CHECK-NEXT: store i8* %{{.*}}, i8** [[_CMDADDR]],
|
|
|
|
// self nil-check
|
|
// CHECK-NEXT: [[SELF:%.*]] = load %0*, %0** [[SELFADDR]],
|
|
// CHECK-NEXT: [[NILCHECK:%.*]] = icmp eq %0* [[SELF]], null
|
|
// CHECK-NEXT: br i1 [[NILCHECK]],
|
|
|
|
// setting return value to nil
|
|
// CHECK-LABEL: objc_direct_method.self_is_nil:
|
|
// CHECK: [[RET0:%.*]] = bitcast{{.*}}[[RETVAL]]
|
|
// CHECK-NEXT: call void @llvm.memset{{[^(]*}}({{[^,]*}}[[RET0]], i8 0,
|
|
// CHECK-NEXT: br label
|
|
|
|
// set value
|
|
// CHECK-LABEL: objc_direct_method.cont:
|
|
// CHECK: [[RET1:%.*]] = bitcast{{.*}}[[RETVAL]]
|
|
// CHECK-NEXT: call void @llvm.memcpy{{[^(]*}}({{[^,]*}}[[RET1]],
|
|
// CHECK-NEXT: br label
|
|
|
|
// return
|
|
// CHECK-LABEL: return:
|
|
// CHECK: [[RET2:%.*]] = bitcast{{.*}}[[RETVAL]]
|
|
// CHECK-NEXT: {{%.*}} = load{{.*}}[[RET2]],
|
|
// CHECK-NEXT: ret
|
|
struct my_complex_struct st = {.a = 42};
|
|
return st;
|
|
}
|
|
|
|
// CHECK-LABEL: define hidden i64 @"\01+[Root classGetComplex]"(
|
|
+ (struct my_complex_struct)classGetComplex __attribute__((objc_direct)) {
|
|
struct my_complex_struct st = {.a = 42};
|
|
return st;
|
|
// CHECK: ret i64
|
|
}
|
|
|
|
// CHECK-LABEL: define hidden void @"\01-[Root getAggregate]"(
|
|
- (struct my_aggregate_struct)getAggregate __attribute__((objc_direct)) {
|
|
// CHECK: %struct.my_aggregate_struct* noalias sret [[RETVAL:%[^,]*]],
|
|
|
|
// loading parameters
|
|
// CHECK-LABEL: entry:
|
|
// CHECK-NEXT: [[SELFADDR:%.*]] = alloca %0*,
|
|
// CHECK-NEXT: [[_CMDADDR:%.*]] = alloca i8*,
|
|
// CHECK-NEXT: store %0* %{{.*}}, %0** [[SELFADDR]],
|
|
// CHECK-NEXT: store i8* %{{.*}}, i8** [[_CMDADDR]],
|
|
|
|
// self nil-check
|
|
// CHECK-NEXT: [[SELF:%.*]] = load %0*, %0** [[SELFADDR]],
|
|
// CHECK-NEXT: [[NILCHECK:%.*]] = icmp eq %0* [[SELF]], null
|
|
// CHECK-NEXT: br i1 [[NILCHECK]],
|
|
|
|
// setting return value to nil
|
|
// CHECK-LABEL: objc_direct_method.self_is_nil:
|
|
// CHECK: [[RET0:%.*]] = bitcast{{.*}}[[RETVAL]]
|
|
// CHECK-NEXT: call void @llvm.memset{{[^(]*}}({{[^,]*}}[[RET0]], i8 0,
|
|
// CHECK-NEXT: br label
|
|
|
|
// set value
|
|
// CHECK-LABEL: objc_direct_method.cont:
|
|
// CHECK: [[RET1:%.*]] = bitcast{{.*}}[[RETVAL]]
|
|
// CHECK: br label
|
|
|
|
// return
|
|
// CHECK-LABEL: return:
|
|
// CHECK: ret void
|
|
struct my_aggregate_struct st = {.a = 42};
|
|
return st;
|
|
}
|
|
|
|
// CHECK-LABEL: define hidden void @"\01+[Root classGetAggregate]"(
|
|
+ (struct my_aggregate_struct)classGetAggregate __attribute__((objc_direct)) {
|
|
struct my_aggregate_struct st = {.a = 42};
|
|
return st;
|
|
// CHECK: ret void
|
|
}
|
|
|
|
@end
|
|
// CHECK-LABEL: define hidden i32 @"\01-[Root intProperty]"
|
|
|
|
@interface Foo : Root {
|
|
id __strong _cause_cxx_destruct;
|
|
}
|
|
@property(nonatomic, readonly, direct) int getDirect_setDynamic;
|
|
@property(nonatomic, readonly) int getDynamic_setDirect;
|
|
@end
|
|
|
|
@interface Foo ()
|
|
@property(nonatomic, readwrite) int getDirect_setDynamic;
|
|
@property(nonatomic, readwrite, direct) int getDynamic_setDirect;
|
|
- (int)directMethodInExtension __attribute__((objc_direct));
|
|
@end
|
|
|
|
@interface Foo (Cat)
|
|
- (int)directMethodInCategory __attribute__((objc_direct));
|
|
@end
|
|
|
|
__attribute__((objc_direct_members))
|
|
@implementation Foo
|
|
// CHECK-LABEL: define hidden i32 @"\01-[Foo directMethodInExtension]"(
|
|
- (int)directMethodInExtension {
|
|
return 42;
|
|
}
|
|
// CHECK-LABEL: define hidden i32 @"\01-[Foo getDirect_setDynamic]"(
|
|
// CHECK-LABEL: define internal void @"\01-[Foo setGetDirect_setDynamic:]"(
|
|
// CHECK-LABEL: define internal i32 @"\01-[Foo getDynamic_setDirect]"(
|
|
// CHECK-LABEL: define hidden void @"\01-[Foo setGetDynamic_setDirect:]"(
|
|
// CHECK-LABEL: define internal void @"\01-[Foo .cxx_destruct]"(
|
|
@end
|
|
|
|
@implementation Foo (Cat)
|
|
// CHECK-LABEL: define hidden i32 @"\01-[Foo directMethodInCategory]"(
|
|
- (int)directMethodInCategory {
|
|
return 42;
|
|
}
|
|
// CHECK-LABEL: define hidden i32 @"\01-[Foo directMethodInCategoryNoDecl]"(
|
|
- (int)directMethodInCategoryNoDecl __attribute__((objc_direct)) {
|
|
return 42;
|
|
}
|
|
@end
|
|
|
|
int useRoot(Root *r) {
|
|
// CHECK-LABEL: define i32 @useRoot
|
|
// CHECK: %{{[^ ]*}} = call i32 bitcast {{.*}} @"\01-[Root getInt]"
|
|
// CHECK: %{{[^ ]*}} = call i32 bitcast {{.*}} @"\01-[Root intProperty]"
|
|
// CHECK: %{{[^ ]*}} = call i32 bitcast {{.*}} @"\01-[Root intProperty2]"
|
|
return [r getInt] + [r intProperty] + [r intProperty2];
|
|
}
|
|
|
|
int useFoo(Foo *f) {
|
|
// CHECK-LABEL: define i32 @useFoo
|
|
// CHECK: call void bitcast {{.*}} @"\01-[Foo setGetDynamic_setDirect:]"
|
|
// CHECK: %{{[^ ]*}} = call i32 bitcast {{.*}} @"\01-[Foo getDirect_setDynamic]"
|
|
// CHECK: %{{[^ ]*}} = call i32 bitcast {{.*}} @"\01-[Foo directMethodInExtension]"
|
|
// CHECK: %{{[^ ]*}} = call i32 bitcast {{.*}} @"\01-[Foo directMethodInCategory]"
|
|
// CHECK: %{{[^ ]*}} = call i32 bitcast {{.*}} @"\01-[Foo directMethodInCategoryNoDecl]"
|
|
[f setGetDynamic_setDirect:1];
|
|
return [f getDirect_setDynamic] +
|
|
[f directMethodInExtension] +
|
|
[f directMethodInCategory] +
|
|
[f directMethodInCategoryNoDecl];
|
|
}
|
|
|
|
__attribute__((objc_root_class))
|
|
@interface RootDeclOnly
|
|
@property(direct, readonly) int intProperty;
|
|
@end
|
|
|
|
int useRootDeclOnly(RootDeclOnly *r) {
|
|
// CHECK-LABEL: define i32 @useRootDeclOnly
|
|
// CHECK: %{{[^ ]*}} = call i32 bitcast {{.*}} @"\01-[RootDeclOnly intProperty]"
|
|
return [r intProperty];
|
|
}
|