Skip to content

Commit 4dd3c10

Browse files
committed
[IRGen] Correctly emit metadata for ObjC class properties.
...which is important because Clang CodeGen was already claiming we were! rdar://problem/25473573
1 parent 1046f3c commit 4dd3c10

File tree

4 files changed

+187
-44
lines changed

4 files changed

+187
-44
lines changed

lib/IRGen/GenClass.cpp

Lines changed: 65 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,8 @@ namespace {
770770
SmallVector<llvm::Constant*, 16> OptInstanceMethods;
771771
SmallVector<llvm::Constant*, 16> OptClassMethods;
772772
SmallVector<llvm::Constant*, 4> Protocols;
773-
SmallVector<llvm::Constant*, 8> Properties;
773+
SmallVector<llvm::Constant*, 8> InstanceProperties;
774+
SmallVector<llvm::Constant*, 8> ClassProperties;
774775
SmallVector<llvm::Constant*, 8> InstanceMethodTypesExt;
775776
SmallVector<llvm::Constant*, 8> ClassMethodTypesExt;
776777
SmallVector<llvm::Constant*, 8> OptInstanceMethodTypesExt;
@@ -918,74 +919,93 @@ namespace {
918919
public:
919920
llvm::Constant *emitCategory() {
920921
assert(TheExtension && "can't emit category data for a class");
921-
SmallVector<llvm::Constant*, 11> fields;
922+
llvm::Constant *fields[8];
922923
// struct category_t {
923924
// char const *name;
924-
fields.push_back(IGM.getAddrOfGlobalString(CategoryName));
925+
fields[0] = IGM.getAddrOfGlobalString(CategoryName);
925926
// const class_t *theClass;
926927
if (getClass()->hasClangNode())
927-
fields.push_back(IGM.getAddrOfObjCClass(getClass(), NotForDefinition));
928+
fields[1] = IGM.getAddrOfObjCClass(getClass(), NotForDefinition);
928929
else {
929930
auto type = getSelfType(getClass()).getSwiftRValueType();
930931
llvm::Constant *metadata =
931932
tryEmitConstantHeapMetadataRef(IGM, type, /*allowUninit*/ true);
932933
assert(metadata &&
933934
"extended objc class doesn't have constant metadata?");
934-
fields.push_back(metadata);
935+
fields[1] = metadata;
935936
}
936937
// const method_list_t *instanceMethods;
937-
fields.push_back(buildInstanceMethodList());
938+
fields[2] = buildInstanceMethodList();
938939
// const method_list_t *classMethods;
939-
fields.push_back(buildClassMethodList());
940+
fields[3] = buildClassMethodList();
940941
// const protocol_list_t *baseProtocols;
941-
fields.push_back(buildProtocolList());
942+
fields[4] = buildProtocolList();
942943
// const property_list_t *properties;
943-
fields.push_back(buildPropertyList());
944+
fields[5] = buildPropertyList(ForClass);
945+
// const property_list_t *classProperties;
946+
fields[6] = buildPropertyList(ForMetaClass);
947+
// uint32_t size;
948+
auto sizeofPointer = IGM.getPointerSize().getValue();
949+
unsigned size = sizeofPointer * llvm::array_lengthof(fields);
950+
// Adjust for 'size', which is always 32 bits rather than pointer-sized.
951+
// FIXME: Clang does this by using non-ad-hoc types for ObjC runtime
952+
// structures.
953+
size -= (sizeofPointer - 4) * 1;
954+
fields[7] = llvm::ConstantInt::get(IGM.Int32Ty, size);
944955
// };
945-
956+
946957
return buildGlobalVariable(fields, "_CATEGORY_");
947958
}
948959

949960
llvm::Constant *emitProtocol() {
950-
SmallVector<llvm::Constant*, 11> fields;
961+
llvm::Constant *fields[13];
951962
llvm::SmallString<64> nameBuffer;
952963

953964
assert(isBuildingProtocol() && "not emitting a protocol");
954965

955966
// struct protocol_t {
956967
// Class super;
957-
fields.push_back(null());
968+
fields[0] = null();
958969
// char const *name;
959-
fields.push_back(IGM.getAddrOfGlobalString(getEntityName(nameBuffer)));
970+
fields[1] = IGM.getAddrOfGlobalString(getEntityName(nameBuffer));
960971
// const protocol_list_t *baseProtocols;
961-
fields.push_back(buildProtocolList());
972+
fields[2] = buildProtocolList();
962973
// const method_list_t *requiredInstanceMethods;
963-
fields.push_back(buildInstanceMethodList());
974+
fields[3] = buildInstanceMethodList();
964975
// const method_list_t *requiredClassMethods;
965-
fields.push_back(buildClassMethodList());
976+
fields[4] = buildClassMethodList();
966977
// const method_list_t *optionalInstanceMethods;
967-
fields.push_back(buildOptInstanceMethodList());
978+
fields[5] = buildOptInstanceMethodList();
968979
// const method_list_t *optionalClassMethods;
969-
fields.push_back(buildOptClassMethodList());
980+
fields[6] = buildOptClassMethodList();
970981
// const property_list_t *properties;
971-
fields.push_back(buildPropertyList());
982+
fields[7] = buildPropertyList(ForClass);
983+
972984
// uint32_t size;
973-
unsigned size = IGM.getPointerSize().getValue() * fields.size() +
974-
IGM.getPointerSize().getValue(); // This is for extendedMethodTypes
975-
size += 8; // 'size' and 'flags' fields that haven't been added yet.
976-
fields.push_back(llvm::ConstantInt::get(IGM.Int32Ty, size));
985+
auto sizeofPointer = IGM.getPointerSize().getValue();
986+
unsigned size = sizeofPointer * llvm::array_lengthof(fields);
987+
// Adjust for 'size' and 'flags', which are always 32 bits rather than
988+
// pointer-sized.
989+
// FIXME: Clang does this by using non-ad-hoc types for ObjC runtime
990+
// structures.
991+
size -= (sizeofPointer - 4) * 2;
992+
fields[8] = llvm::ConstantInt::get(IGM.Int32Ty, size);
993+
977994
// uint32_t flags;
978995
auto flags = ProtocolDescriptorFlags()
979996
.withSwift(!getProtocol()->hasClangNode())
980997
.withClassConstraint(ProtocolClassConstraint::Class)
981998
.withDispatchStrategy(ProtocolDispatchStrategy::ObjC)
982999
.withSpecialProtocol(getSpecialProtocolID(getProtocol()));
9831000

984-
fields.push_back(llvm::ConstantInt::get(IGM.Int32Ty, flags.getIntValue()));
985-
986-
// const char ** extendedMethodTypes;
987-
fields.push_back(buildOptExtendedMethodTypes());
1001+
fields[9] = llvm::ConstantInt::get(IGM.Int32Ty, flags.getIntValue());
9881002

1003+
// const char ** extendedMethodTypes;
1004+
fields[10] = buildOptExtendedMethodTypes();
1005+
// const char *demangledName;
1006+
fields[11] = null();
1007+
// const property_list_t *classProperties;
1008+
fields[12] = buildPropertyList(ForMetaClass);
9891009
// };
9901010

9911011
return buildGlobalVariable(fields, "_PROTOCOL_");
@@ -1062,7 +1082,7 @@ namespace {
10621082
fields.push_back(null());
10631083

10641084
// const property_list_t *baseProperties;
1065-
fields.push_back(forMeta ? null() : buildPropertyList());
1085+
fields.push_back(buildPropertyList(forMeta));
10661086

10671087
// };
10681088

@@ -1443,10 +1463,11 @@ namespace {
14431463
/// Properties need to be collected in the properties list.
14441464
void visitProperty(VarDecl *var) {
14451465
if (requiresObjCPropertyDescriptor(IGM, var)) {
1446-
// ObjC doesn't support formal class properties.
1447-
if (!var->isStatic())
1448-
if (llvm::Constant *prop = buildProperty(var))
1449-
Properties.push_back(prop);
1466+
if (llvm::Constant *prop = buildProperty(var)) {
1467+
auto &properties =
1468+
(var->isStatic() ? ClassProperties : InstanceProperties);
1469+
properties.push_back(prop);
1470+
}
14501471

14511472
// Don't emit getter/setter descriptors for @NSManaged properties.
14521473
if (var->getAttrs().hasAttribute<NSManagedAttr>() ||
@@ -1569,12 +1590,19 @@ namespace {
15691590
/// };
15701591
///
15711592
/// This method does not return a value of a predictable type.
1572-
llvm::Constant *buildPropertyList() {
1593+
llvm::Constant *buildPropertyList(ForMetaClass_t classOrMeta) {
15731594
Size eltSize = 2 * IGM.getPointerSize();
1574-
return buildOptionalList(Properties, eltSize,
1575-
chooseNamePrefix("_PROPERTIES_",
1576-
"_CATEGORY_PROPERTIES_",
1577-
"_PROTOCOL_PROPERTIES_"));
1595+
StringRef namePrefix;
1596+
if (classOrMeta == ForClass) {
1597+
return buildOptionalList(InstanceProperties, eltSize,
1598+
chooseNamePrefix("_PROPERTIES_",
1599+
"_CATEGORY_PROPERTIES_",
1600+
"_PROTOCOL_PROPERTIES_"));
1601+
}
1602+
return buildOptionalList(ClassProperties, eltSize,
1603+
chooseNamePrefix("_CLASS_PROPERTIES_",
1604+
"_CATEGORY_CLASS_PROPERTIES_",
1605+
"_PROTOCOL_CLASS_PROPERTIES_"));
15781606
}
15791607

15801608
/*** General ***********************************************************/

test/IRGen/objc_properties.swift

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
bareIvar = SomeObject()
2828
wibble = SomeObject()
2929
}
30+
31+
static var sharedProp: Int64 = 0
3032
}
3133

3234
extension SomeObject {
@@ -38,6 +40,10 @@ extension SomeObject {
3840
bareIvar = self
3941
}
4042
}
43+
44+
class var extensionClassProp : SomeObject.Type {
45+
return self
46+
}
4147
}
4248

4349
// <rdar://problem/16952186> Crash with @lazy in @objc class
@@ -62,7 +68,10 @@ class Class17127126 {
6268
lazy var x = 1
6369
}
6470

65-
71+
@objc protocol Proto {
72+
var value: Int { get }
73+
static var sharedInstance: AnyObject { get set }
74+
}
6675

6776

6877

@@ -82,6 +91,27 @@ class Class17127126 {
8291
// CHECK: [[WIBBLE_NAME:@.*]] = private unnamed_addr constant [7 x i8] c"wobble\00"
8392
// CHECK: [[WIBBLE_ATTRS:@.*]] = private unnamed_addr constant [50 x i8] c"T@\22_TtC15objc_properties10SomeObject\22,N,&,Vwibble\00"
8493

94+
// CHECK: [[SHARED_NAME:@.*]] = private unnamed_addr constant [11 x i8] c"sharedProp\00"
95+
// CHECK: [[SHARED_ATTRS:@.*]] = private unnamed_addr constant [17 x i8] c"Tq,N,VsharedProp\00"
96+
97+
// CHECK: @_CLASS_PROPERTIES__TtC15objc_properties10SomeObject = private constant { {{.*}}] } {
98+
// CHECK: i32 16,
99+
// CHECK: i32 1,
100+
// CHECK: [1 x { i8*, i8* }] [{
101+
// CHECK: i8* getelementptr inbounds ([11 x i8], [11 x i8]* [[SHARED_NAME]], i64 0, i64 0),
102+
// CHECK: i8* getelementptr inbounds ([17 x i8], [17 x i8]* [[SHARED_ATTRS]], i64 0, i64 0)
103+
// CHECK: }]
104+
// CHECK: }, section "__DATA, __objc_const", align 8
105+
106+
// CHECK: @_METACLASS_DATA__TtC15objc_properties10SomeObject = private constant { {{.*}} } {
107+
// CHECK-SAME: i32 {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}},
108+
// CHECK-SAME: i8* null,
109+
// CHECK-SAME: i8* getelementptr inbounds ([{{.+}} x i8], [{{.+}} x i8]* {{@.+}}, i64 0, i64 0),
110+
// CHECK-SAME: { {{.+}} }* @_CLASS_METHODS__TtC15objc_properties10SomeObject,
111+
// CHECK-SAME: i8* null, i8* null, i8* null,
112+
// CHECK-SAME: { {{.+}} }* @_CLASS_PROPERTIES__TtC15objc_properties10SomeObject
113+
// CHECK-SAME: }, section "__DATA, __objc_const", align 8
114+
85115
// CHECK: @_INSTANCE_METHODS__TtC15objc_properties10SomeObject = private constant { {{.*}}] } {
86116
// CHECK: i32 24,
87117
// CHECK: i32 8,
@@ -138,8 +168,22 @@ class Class17127126 {
138168
// CHECK: }]
139169
// CHECK: }, section "__DATA, __objc_const", align 8
140170

171+
// CHECK: @_DATA__TtC15objc_properties10SomeObject = private constant { {{.+}} } {
172+
// CHECK: i32 {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}},
173+
// CHECK: i8* null,
174+
// CHECK: i8* getelementptr inbounds ([{{.+}} x i8], [{{.+}} x i8]* {{@.+}}, i64 0, i64 0),
175+
// CHECK: { {{.+}} }* @_INSTANCE_METHODS__TtC15objc_properties10SomeObject,
176+
// CHECK: i8* null,
177+
// CHECK: { {{.+}} }* @_IVARS__TtC15objc_properties10SomeObject,
178+
// CHECK: i8* null,
179+
// CHECK: { {{.+}} }* @_PROPERTIES__TtC15objc_properties10SomeObject
180+
// CHECK: }, section "__DATA, __objc_const", align 8
181+
141182
// CHECK: [[EXTENSIONPROPERTY_NAME:@.*]] = private unnamed_addr constant [18 x i8] c"extensionProperty\00"
142183

184+
// CHECK: [[EXTENSIONCLASSPROPERTY_NAME:@.*]] = private unnamed_addr constant [19 x i8] c"extensionClassProp\00"
185+
// CHECK: [[EXTENSIONCLASSPROPERTY_ATTRS:@.*]] = private unnamed_addr constant [7 x i8] c"T#,N,R\00"
186+
143187
// CHECK: @"_CATEGORY_INSTANCE_METHODS__TtC15objc_properties10SomeObject_$_objc_properties" = private constant { {{.*}}] } {
144188
// CHECK: i32 24,
145189
// CHECK: i32 2,
@@ -163,10 +207,71 @@ class Class17127126 {
163207
// CHECK: }]
164208
// CHECK: }, section "__DATA, __objc_const", align 8
165209

210+
// CHECK: @"_CATEGORY_CLASS_PROPERTIES__TtC15objc_properties10SomeObject_$_objc_properties" = private constant { {{.*}}] } {
211+
// CHECK: i32 16,
212+
// CHECK: i32 1,
213+
// CHECK: [1 x { i8*, i8* }] [{
214+
// CHECK: i8* getelementptr inbounds ([19 x i8], [19 x i8]* [[EXTENSIONCLASSPROPERTY_NAME]], i64 0, i64 0),
215+
// CHECK: i8* getelementptr inbounds ([7 x i8], [7 x i8]* [[EXTENSIONCLASSPROPERTY_ATTRS]], i64 0, i64 0)
216+
// CHECK: }]
217+
// CHECK: }, section "__DATA, __objc_const", align 8
218+
219+
// CHECK: @"_CATEGORY__TtC15objc_properties10SomeObject_$_objc_properties" = private constant { {{.+}} } {
220+
// CHECK: i8* getelementptr inbounds ([{{.+}} x i8], [{{.+}} x i8]* {{@.+}}, i64 0, i64 0),
221+
// CHECK: %swift.type* bitcast (i64* getelementptr inbounds (<{ {{.+}} }>* @_TMfC15objc_properties10SomeObject, i32 0, i32 2) to %swift.type*),
222+
// CHECK: { {{.+}} }* @"_CATEGORY_INSTANCE_METHODS__TtC15objc_properties10SomeObject_$_objc_properties",
223+
// CHECK: { {{.+}} }* @"_CATEGORY_CLASS_METHODS__TtC15objc_properties10SomeObject_$_objc_properties",
224+
// CHECK: i8* null,
225+
// CHECK: { {{.+}} }* @"_CATEGORY_PROPERTIES__TtC15objc_properties10SomeObject_$_objc_properties",
226+
// CHECK: { {{.+}} }* @"_CATEGORY_CLASS_PROPERTIES__TtC15objc_properties10SomeObject_$_objc_properties",
227+
// CHECK: i32 60
228+
// CHECK: }, section "__DATA, __objc_const", align 8
229+
230+
166231
// CHECK: @_INSTANCE_METHODS__TtC15objc_properties4Tree =
167232
// CHECK: i8* getelementptr inbounds ([7 x i8], [7 x i8]* @"\01L_selector_data(parent)", i64 0, i64 0),
168233
// CHECK: i8* getelementptr inbounds ([8 x i8], [8 x i8]* [[GETTER_SIGNATURE]], i64 0, i64 0),
169234
// CHECK: i8* bitcast (%2* (%2*, i8*)* @_TToFC15objc_properties4Treeg6parentXwGSqS0__ to i8*)
170235
// CHECK: i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"\01L_selector_data(setParent:)", i64 0, i64 0),
171236
// CHECK: i8* getelementptr inbounds ([11 x i8], [11 x i8]* [[SETTER_SIGNATURE]], i64 0, i64 0),
172237
// CHECK: i8* bitcast (void (%2*, i8*, %2*)* @_TToFC15objc_properties4Trees6parentXwGSqS0__ to i8*)
238+
239+
// CHECK: @_PROTOCOL__TtP15objc_properties5Proto_ = private constant { {{.+}} } {
240+
// CHECK: i8* null,
241+
// CHECK: i8* getelementptr inbounds ([{{.+}} x i8], [{{.+}} x i8]* {{@.+}}, i64 0, i64 0),
242+
// CHECK: i8* null,
243+
// CHECK: { {{.+}} }* @_PROTOCOL_INSTANCE_METHODS__TtP15objc_properties5Proto_,
244+
// CHECK: { {{.+}} }* @_PROTOCOL_CLASS_METHODS__TtP15objc_properties5Proto_,
245+
// CHECK: i8* null,
246+
// CHECK: i8* null,
247+
// CHECK: { {{.+}} }* @_PROTOCOL_PROPERTIES__TtP15objc_properties5Proto_,
248+
// CHECK: i32 96, i32 1,
249+
// CHECK: { {{.+}} }* @_PROTOCOL_METHOD_TYPES__TtP15objc_properties5Proto_,
250+
// CHECK: i8* null,
251+
// CHECK: { {{.+}} }* @_PROTOCOL_CLASS_PROPERTIES__TtP15objc_properties5Proto_
252+
// CHECK: }, section "__DATA, __objc_const", align 8
253+
254+
255+
// CHECK: [[PROTOCOLPROPERTY_NAME:@.+]] = private unnamed_addr constant [6 x i8] c"value\00"
256+
// CHECK: [[PROTOCOLPROPERTY_ATTRS:@.+]] = private unnamed_addr constant [7 x i8] c"Tq,N,R\00"
257+
258+
// CHECK: [[PROTOCOLCLASSPROPERTY_NAME:@.+]] = private unnamed_addr constant [15 x i8] c"sharedInstance\00"
259+
// CHECK: [[PROTOCOLCLASSPROPERTY_ATTRS:@.+]] = private unnamed_addr constant [7 x i8] c"T@,N,&\00"
260+
261+
// CHECK: @_PROTOCOL_PROPERTIES__TtP15objc_properties5Proto_ = private constant { {{.*}}] } {
262+
// CHECK: i32 16,
263+
// CHECK: i32 1,
264+
// CHECK: [1 x { i8*, i8* }] [{
265+
// CHECK: i8* getelementptr inbounds ([6 x i8], [6 x i8]* [[PROTOCOLPROPERTY_NAME]], i64 0, i64 0),
266+
// CHECK: i8* getelementptr inbounds ([7 x i8], [7 x i8]* [[PROTOCOLPROPERTY_ATTRS]], i64 0, i64 0)
267+
// CHECK: }]
268+
// CHECK: }, section "__DATA, __objc_const", align 8
269+
270+
// CHECK: @_PROTOCOL_CLASS_PROPERTIES__TtP15objc_properties5Proto_ = private constant { {{.*}}] } {
271+
// CHECK: i32 16,
272+
// CHECK: i32 1,
273+
// CHECK: [1 x { i8*, i8* }] [{
274+
// CHECK: i8* getelementptr inbounds ([15 x i8], [15 x i8]* [[PROTOCOLCLASSPROPERTY_NAME]], i64 0, i64 0),
275+
// CHECK: i8* getelementptr inbounds ([7 x i8], [7 x i8]* [[PROTOCOLCLASSPROPERTY_ATTRS]], i64 0, i64 0)
276+
// CHECK: }]
277+
// CHECK: }, section "__DATA, __objc_const", align 8

test/IRGen/protocol_metadata.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,20 @@ protocol ABO : A, B, O { func abo() }
3737
// CHECK: }
3838

3939
// -- @objc protocol O uses ObjC symbol mangling and layout
40-
// CHECK: @_PROTOCOL__TtP17protocol_metadata1O_ = private constant { {{.*}} i32, { [1 x i8*] }* } {
40+
// CHECK: @_PROTOCOL__TtP17protocol_metadata1O_ = private constant { {{.*}} i32, { [1 x i8*] }*, i8*, i8* } {
4141
// CHECK: @_PROTOCOL_INSTANCE_METHODS__TtP17protocol_metadata1O_,
42-
// -- flags: 1 = Swift
43-
// CHECK: i32 80, i32 1
42+
// -- size, flags: 1 = Swift
43+
// CHECK: i32 96, i32 1
4444
// CHECK: }
4545
// CHECK: @_PROTOCOL_METHOD_TYPES__TtP17protocol_metadata1O_
4646
// CHECK: }
4747

4848
// -- @objc protocol OPT uses ObjC symbol mangling and layout
49-
// CHECK: @_PROTOCOL__TtP17protocol_metadata3OPT_ = private constant { {{.*}} i32, { [4 x i8*] }* } {
49+
// CHECK: @_PROTOCOL__TtP17protocol_metadata3OPT_ = private constant { {{.*}} i32, { [4 x i8*] }*, i8*, i8* } {
5050
// CHECK: @_PROTOCOL_INSTANCE_METHODS_OPT__TtP17protocol_metadata3OPT_,
5151
// CHECK: @_PROTOCOL_CLASS_METHODS_OPT__TtP17protocol_metadata3OPT_,
52-
// -- flags: 1 = Swift
53-
// CHECK: i32 80, i32 1
52+
// -- size, flags: 1 = Swift
53+
// CHECK: i32 96, i32 1
5454
// CHECK: }
5555
// CHECK: @_PROTOCOL_METHOD_TYPES__TtP17protocol_metadata3OPT_
5656
// CHECK: }

test/Interpreter/objc_class_properties.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,5 +208,15 @@ ClassProperties.test("namingConflict/protocol") {
208208
expectEmpty(type.protoProp)
209209
}
210210

211+
ClassProperties.test("runtime") {
212+
let theClass: AnyObject = SwiftClass.self
213+
let prop = class_getProperty(object_getClass(theClass), "value")
214+
expectTrue(prop != nil)
215+
216+
let nameAsCString = property_getName(prop)
217+
expectTrue(nameAsCString != nil)
218+
expectEqual("value", String(cString: nameAsCString))
219+
}
220+
211221
runAllTests()
212222

0 commit comments

Comments
 (0)