Skip to content

Commit fd0cac5

Browse files
committed
IRGen: Fix crash with empty-sized field in a class with resilient ancestry
The problem was that HasObjCAncestry was not getting set if HasResilientAncestry was true, and thus emitFieldOffsetGlobals() was marking the field offset as const even though the Objective-C runtime might slide it. Fixes <rdar://problem/48031465>.
1 parent 6abd588 commit fd0cac5

File tree

5 files changed

+99
-7
lines changed

5 files changed

+99
-7
lines changed

lib/IRGen/GenClass.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,13 @@ namespace {
273273
// context.
274274
if (superclassType.hasArchetype())
275275
Options |= ClassMetadataFlags::ClassHasGenericLayout;
276+
277+
// Since we're not going to visit the superclass, make sure that we still
278+
// set ClassHasObjCAncestry correctly.
279+
if (superclassType.getASTType()->getReferenceCounting()
280+
== ReferenceCounting::ObjC) {
281+
Options |= ClassMetadataFlags::ClassHasObjCAncestry;
282+
}
276283
} else {
277284
// Otherwise, we are allowed to have total knowledge of the superclass
278285
// fields, so walk them to compute the layout.

test/IRGen/class_resilience_objc.swift

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
// RUN: %empty-directory(%t)
22
// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_struct.swiftmodule -module-name=resilient_struct %S/../Inputs/resilient_struct.swift
3-
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %t -enable-library-evolution -enable-objc-interop -emit-ir -o - -primary-file %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize -DINT=i%target-ptrsize
3+
// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_objc_class.swiftmodule -module-name=resilient_objc_class %S/../Inputs/resilient_objc_class.swift -I %t
4+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %t -enable-library-evolution -emit-ir -o - -primary-file %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize -DINT=i%target-ptrsize
45

5-
// XFAIL: CPU=armv7k
6-
// XFAIL: CPU=powerpc64le
7-
// XFAIL: CPU=s390x
6+
// REQUIRES: objc_interop
87

98
import Foundation
109
import resilient_struct
10+
import resilient_objc_class
1111

1212
// Note that these are all mutable to allow for the runtime to slide them.
1313
// CHECK: @"$s21class_resilience_objc27ClassWithEmptyThenResilientC9resilient0I7_struct0H3IntVvpWvd" = hidden global [[INT]] 0,
1414
// CHECK: @"$s21class_resilience_objc27ClassWithResilientThenEmptyC9resilient0I7_struct0F3IntVvpWvd" = hidden global [[INT]] 0,
15+
// CHECK: @"$s21class_resilience_objc34AnotherClassWithEmptyThenResilientC9resilient0J7_struct0I3IntVvpWvd" = hidden global [[INT]] 0,
16+
// CHECK: @"$s21class_resilience_objc34AnotherClassWithResilientThenEmptyC9resilient0J7_struct0G3IntVvpWvd" = hidden global [[INT]] 0,
17+
1518
// CHECK: @"$s21class_resilience_objc27ClassWithEmptyThenResilientC5emptyAA0F0VvpWvd" = hidden global [[INT]] 0,
1619
// CHECK: @"$s21class_resilience_objc27ClassWithResilientThenEmptyC5emptyAA0H0VvpWvd" = hidden global [[INT]] 0,
20+
// CHECK: @"$s21class_resilience_objc34AnotherClassWithEmptyThenResilientC5emptyAA0G0VvpWvd" = hidden global [[INT]] 0,
21+
// CHECK: @"$s21class_resilience_objc34AnotherClassWithResilientThenEmptyC5emptyAA0I0VvpWvd" = hidden global [[INT]] 0,
1722

1823
public class FixedLayoutObjCSubclass : NSObject {
1924
// This field could use constant direct access because NSObject has
@@ -121,3 +126,26 @@ public class ClassWithResilientThenEmpty : DummyClass {
121126
self.resilient = resilient
122127
}
123128
}
129+
130+
// Same as the above, but the superclass is resilient, and ultimately inherits
131+
// from an Objective-C base class.
132+
133+
public class AnotherClassWithEmptyThenResilient : ResilientNSObjectOutsideParent {
134+
public let empty: Empty
135+
public let resilient: ResilientInt
136+
137+
public init(empty: Empty, resilient: ResilientInt) {
138+
self.empty = empty
139+
self.resilient = resilient
140+
}
141+
}
142+
143+
public class AnotherClassWithResilientThenEmpty : ResilientNSObjectOutsideParent {
144+
public let resilient: ResilientInt
145+
public let empty: Empty
146+
147+
public init(empty: Empty, resilient: ResilientInt) {
148+
self.empty = empty
149+
self.resilient = resilient
150+
}
151+
}

test/Interpreter/Inputs/ObjCClasses/ObjCClasses.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ NS_ASSUME_NONNULL_BEGIN
88
/* This class has instance variables which are not apparent in the
99
interface. Subclasses will need to be slid by the ObjC runtime. */
1010
@interface HasHiddenIvars : NSObject
11+
- (instancetype)init;
1112
@property NSInteger x;
1213
@property NSInteger y;
1314
@property NSInteger z;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import ObjCClasses
2+
3+
open class HasResilientObjCBaseClass : HasHiddenIvars {}

test/Interpreter/objc_class_resilience.swift

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
// RUN: %empty-directory(%t)
22

33
// RUN: %target-clang -fobjc-arc %S/Inputs/ObjCClasses/ObjCClasses.m -c -o %t/ObjCClasses.o
4+
45
// RUN: %target-build-swift-dylib(%t/%target-library-name(resilient_struct)) -enable-library-evolution %S/../Inputs/resilient_struct.swift -emit-module -emit-module-path %t/resilient_struct.swiftmodule -module-name resilient_struct
5-
// RUN: %target-codesign %t/%target-library-name(resilient_struct)
6+
// RUN: %target-build-swift-dylib(%t/%target-library-name(resilient_objc_class)) -enable-library-evolution %S/Inputs/resilient_objc_class.swift -emit-module -emit-module-path %t/resilient_objc_class.swiftmodule -module-name resilient_objc_class -I %S/Inputs/ObjCClasses/ -L %t -Xlinker %t/ObjCClasses.o -framework Foundation
7+
8+
// RUN: %target-codesign %t/%target-library-name(resilient_struct) %t/%target-library-name(resilient_objc_class)
69

7-
// RUN: %target-build-swift %s -L %t -I %t -lresilient_struct -I %S/Inputs/ObjCClasses/ -Xlinker %t/ObjCClasses.o -o %t/main %target-rpath(%t)
10+
// RUN: %target-build-swift %s -L %t -I %t -lresilient_struct -lresilient_objc_class -I %S/Inputs/ObjCClasses/ -o %t/main %target-rpath(%t)
811
// RUN: %target-codesign %t/main
912

10-
// RUN: %target-run %t/main %t/%target-library-name(resilient_struct)
13+
// RUN: %target-run %t/main %t/%target-library-name(resilient_struct) %t/%target-library-name(resilient_objc_class)
1114

1215
// REQUIRES: executable_test
1316
// REQUIRES: objc_interop
1417

1518
import StdlibUnittest
1619
import Foundation
1720
import resilient_struct
21+
import resilient_objc_class
1822
import ObjCClasses
1923

2024

@@ -105,4 +109,53 @@ ResilientClassTestSuite.test("ResilientThenEmpty") {
105109
expectEqual(c.t, 400000)
106110
}
107111

112+
// Same as the above, but the class itself has a resilient base class
113+
class AnotherClassWithEmptyThenResilient : HasResilientObjCBaseClass {
114+
let empty: Empty
115+
let resilient: ResilientInt
116+
117+
init(empty: Empty, resilient: ResilientInt) {
118+
self.empty = empty
119+
self.resilient = resilient
120+
}
121+
}
122+
123+
ResilientClassTestSuite.test("AnotherEmptyThenResilient") {
124+
let c = AnotherClassWithEmptyThenResilient(empty: Empty(),
125+
resilient: ResilientInt(i: 17))
126+
c.x = 100
127+
c.y = 2000
128+
c.z = 30000
129+
c.t = 400000
130+
expectEqual(c.resilient.i, 17)
131+
expectEqual(c.x, 100)
132+
expectEqual(c.y, 2000)
133+
expectEqual(c.z, 30000)
134+
expectEqual(c.t, 400000)
135+
}
136+
137+
class AnotherClassWithResilientThenEmpty : HasHiddenIvars {
138+
let resilient: ResilientInt
139+
let empty: Empty
140+
141+
init(empty: Empty, resilient: ResilientInt) {
142+
self.empty = empty
143+
self.resilient = resilient
144+
}
145+
}
146+
147+
ResilientClassTestSuite.test("AnotherResilientThenEmpty") {
148+
let c = AnotherClassWithResilientThenEmpty(empty: Empty(),
149+
resilient: ResilientInt(i: 17))
150+
c.x = 100
151+
c.y = 2000
152+
c.z = 30000
153+
c.t = 400000
154+
expectEqual(c.resilient.i, 17)
155+
expectEqual(c.x, 100)
156+
expectEqual(c.y, 2000)
157+
expectEqual(c.z, 30000)
158+
expectEqual(c.t, 400000)
159+
}
160+
108161
runAllTests()

0 commit comments

Comments
 (0)