Skip to content

IRGen: Fix crash with empty-sized field in a class with resilient ancestry [5.3] #31351

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/IRGen/GenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,13 @@ namespace {
// context.
if (superclassType.hasArchetype())
Options |= ClassMetadataFlags::ClassHasGenericLayout;

// Since we're not going to visit the superclass, make sure that we still
// set ClassHasObjCAncestry correctly.
if (superclassType.getASTType()->getReferenceCounting()
== ReferenceCounting::ObjC) {
Options |= ClassMetadataFlags::ClassHasObjCAncestry;
}
} else {
// Otherwise, we are allowed to have total knowledge of the superclass
// fields, so walk them to compute the layout.
Expand Down
36 changes: 32 additions & 4 deletions test/IRGen/class_resilience_objc.swift
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
// RUN: %empty-directory(%t)
// 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
// 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
// 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
// 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

// XFAIL: CPU=armv7k
// XFAIL: CPU=powerpc64le
// XFAIL: CPU=s390x
// REQUIRES: objc_interop

import Foundation
import resilient_struct
import resilient_objc_class

// Note that these are all mutable to allow for the runtime to slide them.
// CHECK: @"$s21class_resilience_objc27ClassWithEmptyThenResilientC9resilient0I7_struct0H3IntVvpWvd" = hidden global [[INT]] 0,
// CHECK: @"$s21class_resilience_objc27ClassWithResilientThenEmptyC9resilient0I7_struct0F3IntVvpWvd" = hidden global [[INT]] 0,
// CHECK: @"$s21class_resilience_objc34AnotherClassWithEmptyThenResilientC9resilient0J7_struct0I3IntVvpWvd" = hidden global [[INT]] 0,
// CHECK: @"$s21class_resilience_objc34AnotherClassWithResilientThenEmptyC9resilient0J7_struct0G3IntVvpWvd" = hidden global [[INT]] 0,

// CHECK: @"$s21class_resilience_objc27ClassWithEmptyThenResilientC5emptyAA0F0VvpWvd" = hidden global [[INT]] 0,
// CHECK: @"$s21class_resilience_objc27ClassWithResilientThenEmptyC5emptyAA0H0VvpWvd" = hidden global [[INT]] 0,
// CHECK: @"$s21class_resilience_objc34AnotherClassWithEmptyThenResilientC5emptyAA0G0VvpWvd" = hidden global [[INT]] 0,
// CHECK: @"$s21class_resilience_objc34AnotherClassWithResilientThenEmptyC5emptyAA0I0VvpWvd" = hidden global [[INT]] 0,

public class FixedLayoutObjCSubclass : NSObject {
// This field could use constant direct access because NSObject has
Expand Down Expand Up @@ -121,3 +126,26 @@ public class ClassWithResilientThenEmpty : DummyClass {
self.resilient = resilient
}
}

// Same as the above, but the superclass is resilient, and ultimately inherits
// from an Objective-C base class.

public class AnotherClassWithEmptyThenResilient : ResilientNSObjectOutsideParent {
public let empty: Empty
public let resilient: ResilientInt

public init(empty: Empty, resilient: ResilientInt) {
self.empty = empty
self.resilient = resilient
}
}

public class AnotherClassWithResilientThenEmpty : ResilientNSObjectOutsideParent {
public let resilient: ResilientInt
public let empty: Empty

public init(empty: Empty, resilient: ResilientInt) {
self.empty = empty
self.resilient = resilient
}
}
1 change: 1 addition & 0 deletions test/Interpreter/Inputs/ObjCClasses/ObjCClasses.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ NS_ASSUME_NONNULL_BEGIN
/* This class has instance variables which are not apparent in the
interface. Subclasses will need to be slid by the ObjC runtime. */
@interface HasHiddenIvars : NSObject
- (instancetype)init;
@property NSInteger x;
@property NSInteger y;
@property NSInteger z;
Expand Down
3 changes: 3 additions & 0 deletions test/Interpreter/Inputs/resilient_objc_class.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ObjCClasses

open class HasResilientObjCBaseClass : HasHiddenIvars {}
59 changes: 56 additions & 3 deletions test/Interpreter/objc_class_resilience.swift
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
// RUN: %empty-directory(%t)

// RUN: %target-clang -fobjc-arc %S/Inputs/ObjCClasses/ObjCClasses.m -c -o %t/ObjCClasses.o

// 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
// RUN: %target-codesign %t/%target-library-name(resilient_struct)
// 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

// RUN: %target-codesign %t/%target-library-name(resilient_struct) %t/%target-library-name(resilient_objc_class)

// 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)
// RUN: %target-build-swift %s -L %t -I %t -lresilient_struct -lresilient_objc_class -I %S/Inputs/ObjCClasses/ -o %t/main %target-rpath(%t)
// RUN: %target-codesign %t/main

// RUN: %target-run %t/main %t/%target-library-name(resilient_struct)
// RUN: %target-run %t/main %t/%target-library-name(resilient_struct) %t/%target-library-name(resilient_objc_class)

// REQUIRES: executable_test
// REQUIRES: objc_interop

import StdlibUnittest
import Foundation
import resilient_struct
import resilient_objc_class
import ObjCClasses


Expand Down Expand Up @@ -105,4 +109,53 @@ ResilientClassTestSuite.test("ResilientThenEmpty") {
expectEqual(c.t, 400000)
}

// Same as the above, but the class itself has a resilient base class
class AnotherClassWithEmptyThenResilient : HasResilientObjCBaseClass {
let empty: Empty
let resilient: ResilientInt

init(empty: Empty, resilient: ResilientInt) {
self.empty = empty
self.resilient = resilient
}
}

ResilientClassTestSuite.test("AnotherEmptyThenResilient") {
let c = AnotherClassWithEmptyThenResilient(empty: Empty(),
resilient: ResilientInt(i: 17))
c.x = 100
c.y = 2000
c.z = 30000
c.t = 400000
expectEqual(c.resilient.i, 17)
expectEqual(c.x, 100)
expectEqual(c.y, 2000)
expectEqual(c.z, 30000)
expectEqual(c.t, 400000)
}

class AnotherClassWithResilientThenEmpty : HasHiddenIvars {
let resilient: ResilientInt
let empty: Empty

init(empty: Empty, resilient: ResilientInt) {
self.empty = empty
self.resilient = resilient
}
}

ResilientClassTestSuite.test("AnotherResilientThenEmpty") {
let c = AnotherClassWithResilientThenEmpty(empty: Empty(),
resilient: ResilientInt(i: 17))
c.x = 100
c.y = 2000
c.z = 30000
c.t = 400000
expectEqual(c.resilient.i, 17)
expectEqual(c.x, 100)
expectEqual(c.y, 2000)
expectEqual(c.z, 30000)
expectEqual(c.t, 400000)
}

runAllTests()