mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Unavailable enum elements cannot be instantiated at runtime without invoking UB. Therefore the optimizer can consider a basic block unreachable if its only predecessor is a block that terminates in a switch instruction matching an unavailable enum element. Furthermore, removing the switch instruction cases that refer to unavailable enum elements is _mandatory_ when `-unavailable-decl-optimization=complete` is specified because otherwise lowered IR for these instructions could refer to enum tag accessors that will not be lowered, resulting in a failure during linking. Resolves rdar://113872720.
538 lines
13 KiB
Swift
538 lines
13 KiB
Swift
// RUN: %empty-directory(%t)
|
|
|
|
// 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_enum)) -enable-library-evolution %S/../Inputs/resilient_enum.swift -emit-module -emit-module-path %t/resilient_enum.swiftmodule -module-name resilient_enum -I%t -L%t -lresilient_struct
|
|
// RUN: %target-codesign %t/%target-library-name(resilient_enum)
|
|
|
|
// RUN: %target-build-swift %s -L %t -I %t -lresilient_struct -lresilient_enum -o %t/main %target-rpath(%t)
|
|
// RUN: %target-codesign %t/main
|
|
|
|
// RUN: %target-run %t/main %t/%target-library-name(resilient_struct) %t/%target-library-name(resilient_enum)
|
|
|
|
// Test against libraries built with -whole-module-optimization.
|
|
|
|
// RUN: %target-build-swift-dylib(%t/%target-library-name(resilient_struct_wmo)) -enable-library-evolution %S/../Inputs/resilient_struct.swift -emit-module -emit-module-path %t/resilient_struct.swiftmodule -module-name resilient_struct -whole-module-optimization
|
|
// RUN: %target-codesign %t/%target-library-name(resilient_struct_wmo)
|
|
|
|
// RUN: %target-build-swift-dylib(%t/%target-library-name(resilient_enum_wmo)) -enable-library-evolution %S/../Inputs/resilient_enum.swift -emit-module -emit-module-path %t/resilient_enum.swiftmodule -module-name resilient_enum -I%t -L%t -lresilient_struct_wmo -whole-module-optimization
|
|
// RUN: %target-codesign %t/%target-library-name(resilient_enum_wmo)
|
|
|
|
// RUN: %target-build-swift %s -L %t -I %t -lresilient_struct_wmo -lresilient_enum_wmo -o %t/main2 %target-rpath(%t)
|
|
// RUN: %target-codesign %t/main2
|
|
|
|
// RUN: %target-run %t/main2 %t/%target-library-name(resilient_struct_wmo) %t/%target-library-name(resilient_enum_wmo)
|
|
|
|
// Test with -unavailable-decl-optimization=complete.
|
|
|
|
// RUN: %target-build-swift-dylib(%t/%target-library-name(resilient_struct_udoc)) -enable-library-evolution %S/../Inputs/resilient_struct.swift -emit-module -emit-module-path %t/resilient_struct.swiftmodule -module-name resilient_struct -unavailable-decl-optimization=complete
|
|
// RUN: %target-codesign %t/%target-library-name(resilient_struct_udoc)
|
|
|
|
// RUN: %target-build-swift-dylib(%t/%target-library-name(resilient_enum_udoc)) -enable-library-evolution %S/../Inputs/resilient_enum.swift -emit-module -emit-module-path %t/resilient_enum.swiftmodule -module-name resilient_enum -I%t -L%t -lresilient_struct_udoc -unavailable-decl-optimization=complete
|
|
// RUN: %target-codesign %t/%target-library-name(resilient_enum_udoc)
|
|
|
|
// RUN: %target-build-swift %s -L %t -I %t -lresilient_struct_udoc -lresilient_enum_udoc -o %t/main3 %target-rpath(%t)
|
|
// RUN: %target-codesign %t/main3
|
|
|
|
// RUN: %target-run %t/main3 %t/%target-library-name(resilient_struct_udoc) %t/%target-library-name(resilient_enum_udoc)
|
|
|
|
|
|
// REQUIRES: executable_test
|
|
|
|
import StdlibUnittest
|
|
|
|
|
|
import resilient_enum
|
|
import resilient_struct
|
|
|
|
var ResilientEnumTestSuite = TestSuite("ResilientEnum")
|
|
|
|
ResilientEnumTestSuite.test("ResilientEmptyEnum") {
|
|
let e = ResilientEmptyEnum.X
|
|
let n: Int
|
|
switch e {
|
|
case .X: n = 0
|
|
default: n = -1
|
|
}
|
|
expectEqual(n, 0)
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("ResilientSingletonEnum") {
|
|
let o: AnyObject = ArtClass()
|
|
let e = ResilientSingletonEnum.X(o)
|
|
let n: Int
|
|
switch e {
|
|
case .X(let oo):
|
|
n = 0
|
|
expectTrue(o === oo)
|
|
default:
|
|
n = -1
|
|
}
|
|
expectEqual(n, 0)
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("ResilientSingletonGenericEnum") {
|
|
let o = ArtClass()
|
|
let e = ResilientSingletonGenericEnum.X(o)
|
|
let n: Int
|
|
switch e {
|
|
case .X(let oo):
|
|
n = 0
|
|
expectEqual(o === oo, true)
|
|
default:
|
|
n = -1
|
|
}
|
|
expectEqual(n, 0)
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("ResilientNoPayloadEnum") {
|
|
let a: [ResilientNoPayloadEnum] = [.A, .B, .C]
|
|
let b: [Int] = a.map {
|
|
switch $0 {
|
|
case .A:
|
|
return 0
|
|
case .B:
|
|
return 1
|
|
case .C:
|
|
return 2
|
|
default:
|
|
return -1
|
|
}
|
|
}
|
|
|
|
expectEqual(b, [0, 1, 2])
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("ResilientSinglePayloadEnum") {
|
|
let o = ArtClass()
|
|
let a: [ResilientSinglePayloadEnum] = [.A, .B, .C, .X(o)]
|
|
let b: [Int] = a.map {
|
|
switch $0 {
|
|
case .A:
|
|
return 0
|
|
case .B:
|
|
return 1
|
|
case .C:
|
|
return 2
|
|
case .X(let oo):
|
|
expectTrue(o === oo)
|
|
return 3
|
|
default:
|
|
return -1
|
|
}
|
|
}
|
|
|
|
expectEqual(b, [0, 1, 2, 3])
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("ResilientSinglePayloadGenericEnum") {
|
|
let o = ArtClass()
|
|
let a: [ResilientSinglePayloadGenericEnum<ArtClass>] = [.A, .B, .C, .X(o)]
|
|
let b: [Int] = a.map {
|
|
switch $0 {
|
|
case .A:
|
|
return 0
|
|
case .B:
|
|
return 1
|
|
case .C:
|
|
return 2
|
|
case .X(let oo):
|
|
expectTrue(o === oo)
|
|
return 3
|
|
default:
|
|
return -1
|
|
}
|
|
}
|
|
|
|
expectEqual(b, [0, 1, 2, 3])
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("ResilientMultiPayloadEnum") {
|
|
let a: [ResilientMultiPayloadEnum] =
|
|
[.A, .B, .C, .X(1), .Y(2)]
|
|
let b: [Int] = a.map {
|
|
switch $0 {
|
|
case .A:
|
|
return 0
|
|
case .B:
|
|
return 1
|
|
case .C:
|
|
return 2
|
|
case .X(let x):
|
|
expectEqual(x, 1)
|
|
return 3
|
|
case .Y(let y):
|
|
expectEqual(y, 2)
|
|
return 4
|
|
default:
|
|
return -1
|
|
}
|
|
}
|
|
|
|
expectEqual(b, [0, 1, 2, 3, 4])
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("ResilientMultiPayloadEnumRoundTrip") {
|
|
let a = [0, 1, 2, 3, 4]
|
|
let b = a.map { makeResilientMultiPayloadEnum(1122, i: $0) }
|
|
let c: [Int] = b.map {
|
|
switch $0 {
|
|
case .A:
|
|
return 0
|
|
case .B:
|
|
return 1
|
|
case .C:
|
|
return 2
|
|
case .X(let x):
|
|
expectEqual(x, 1122)
|
|
return 3
|
|
case .Y(let y):
|
|
expectEqual(y, 1122)
|
|
return 4
|
|
default:
|
|
return -1
|
|
}
|
|
}
|
|
|
|
expectEqual(c, a)
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("ResilientMultiPayloadEnumSpareBits") {
|
|
let o1 = ArtClass()
|
|
let o2 = ArtClass()
|
|
let a: [ResilientMultiPayloadEnumSpareBits] =
|
|
[.A, .B, .C, .X(o1), .Y(o2)]
|
|
let b: [Int] = a.map {
|
|
switch $0 {
|
|
case .A:
|
|
return 0
|
|
case .B:
|
|
return 1
|
|
case .C:
|
|
return 2
|
|
case .X(let oo1):
|
|
expectTrue(oo1 === o1)
|
|
return 3
|
|
case .Y(let oo2):
|
|
expectTrue(oo2 === o2)
|
|
return 4
|
|
default:
|
|
return -1
|
|
}
|
|
}
|
|
|
|
expectEqual(b, [0, 1, 2, 3, 4])
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("ResilientMultiPayloadEnumSpareBitsRoundTrip") {
|
|
let o = ArtClass()
|
|
let a = [0, 1, 2, 3, 4]
|
|
let b = a.map { makeResilientMultiPayloadEnumSpareBits(o, i: $0) }
|
|
let c: [Int] = b.map {
|
|
switch $0 {
|
|
case .A:
|
|
return 0
|
|
case .B:
|
|
return 1
|
|
case .C:
|
|
return 2
|
|
case .X(let oo):
|
|
expectTrue(oo === o)
|
|
return 3
|
|
case .Y(let oo):
|
|
expectTrue(oo === o)
|
|
return 4
|
|
default:
|
|
return -1
|
|
}
|
|
}
|
|
|
|
expectEqual(c, a)
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("ResilientMultiPayloadEnumSpareBitsAndExtraBits") {
|
|
let o = ArtClass()
|
|
let s: SevenSpareBits = (false, 1, 2, 3, 4, 5, 6, 7)
|
|
let a: [ResilientMultiPayloadEnumSpareBitsAndExtraBits]
|
|
= [.P1(s), .P2(o), .P3(o), .P4(o), .P5(o), .P6(o), .P7(o), .P8(o)]
|
|
let b: [Int] = a.map {
|
|
switch $0 {
|
|
case .P1(let ss):
|
|
// FIXME: derive Equatable conformances for arbitrary tuples :-)
|
|
expectEqual(ss.0, s.0)
|
|
expectEqual(ss.1, s.1)
|
|
expectEqual(ss.2, s.2)
|
|
expectEqual(ss.3, s.3)
|
|
expectEqual(ss.4, s.4)
|
|
expectEqual(ss.5, s.5)
|
|
expectEqual(ss.6, s.6)
|
|
expectEqual(ss.7, s.7)
|
|
return 0
|
|
case .P2(let oo):
|
|
expectTrue(oo === o)
|
|
return 1
|
|
case .P3(let oo):
|
|
expectTrue(oo === o)
|
|
return 2
|
|
case .P4(let oo):
|
|
expectTrue(oo === o)
|
|
return 3
|
|
case .P5(let oo):
|
|
expectTrue(oo === o)
|
|
return 4
|
|
case .P6(let oo):
|
|
expectTrue(oo === o)
|
|
return 5
|
|
case .P7(let oo):
|
|
expectTrue(oo === o)
|
|
return 6
|
|
case .P8(let oo):
|
|
expectTrue(oo === o)
|
|
return 7
|
|
default:
|
|
return -1
|
|
}
|
|
}
|
|
|
|
expectEqual(b, [0, 1, 2, 3, 4, 5, 6, 7])
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("ResilientMultiPayloadGenericEnum") {
|
|
let o1 = ArtClass()
|
|
let o2 = ArtClass()
|
|
let a: [ResilientMultiPayloadGenericEnum<ArtClass>] =
|
|
[.A, .B, .C, .X(o1), .Y(o2)]
|
|
let b: [Int] = a.map {
|
|
switch $0 {
|
|
case .A:
|
|
return 0
|
|
case .B:
|
|
return 1
|
|
case .C:
|
|
return 2
|
|
case .X(let oo1):
|
|
expectTrue(oo1 === o1)
|
|
return 3
|
|
case .Y(let oo2):
|
|
expectTrue(oo2 === o2)
|
|
return 4
|
|
default:
|
|
return -1
|
|
}
|
|
}
|
|
|
|
expectEqual(b, [0, 1, 2, 3, 4])
|
|
}
|
|
|
|
public func getMetadata() -> Any.Type {
|
|
return Shape.self
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("DynamicLayoutMetatype") {
|
|
do {
|
|
var output = ""
|
|
let expected = "- resilient_enum.Shape #0\n"
|
|
dump(getMetadata(), to: &output)
|
|
expectEqual(output, expected)
|
|
}
|
|
do {
|
|
expectEqual(true, getMetadata() == getMetadata())
|
|
}
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("DynamicLayoutSinglePayload") {
|
|
let s = Size(w: 10, h: 20)
|
|
let a: [SimpleShape] = [.KleinBottle, .Triangle(s)]
|
|
|
|
let b: [Int] = a.map {
|
|
switch $0 {
|
|
case .KleinBottle:
|
|
return 0
|
|
case .Triangle(let s):
|
|
expectEqual(s.w, 10)
|
|
expectEqual(s.h, 20)
|
|
return 1
|
|
}
|
|
}
|
|
|
|
expectEqual(b, [0, 1])
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("DynamicLayoutMultiPayload") {
|
|
let s = Size(w: 10, h: 20)
|
|
let a: [Shape] = [.Point, .Rect(s), .RoundedRect(s, s)]
|
|
|
|
let b: [Int] = a.map {
|
|
switch $0 {
|
|
case .Point:
|
|
return 0
|
|
case .Rect(let s):
|
|
expectEqual(s.w, 10)
|
|
expectEqual(s.h, 20)
|
|
return 1
|
|
case .RoundedRect(let s, let ss):
|
|
expectEqual(s.w, 10)
|
|
expectEqual(s.h, 20)
|
|
expectEqual(ss.w, 10)
|
|
expectEqual(ss.h, 20)
|
|
return 2
|
|
}
|
|
}
|
|
|
|
expectEqual(b, [0, 1, 2])
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("DynamicLayoutMultiPayload2") {
|
|
let c = Color(r: 1, g: 2, b: 3)
|
|
let a: [CustomColor] = [.Black, .White, .Custom(c), .Bespoke(c, c)]
|
|
|
|
let b: [Int] = a.map {
|
|
switch $0 {
|
|
case .Black:
|
|
return 0
|
|
case .White:
|
|
return 1
|
|
case .Custom(let c):
|
|
expectEqual(c.r, 1)
|
|
expectEqual(c.g, 2)
|
|
expectEqual(c.b, 3)
|
|
return 2
|
|
case .Bespoke(let c, let cc):
|
|
expectEqual(c.r, 1)
|
|
expectEqual(c.g, 2)
|
|
expectEqual(c.b, 3)
|
|
expectEqual(cc.r, 1)
|
|
expectEqual(cc.g, 2)
|
|
expectEqual(cc.b, 3)
|
|
return 3
|
|
}
|
|
}
|
|
|
|
expectEqual(b, [0, 1, 2, 3])
|
|
}
|
|
|
|
// Make sure case numbers round-trip if payload has zero size
|
|
|
|
ResilientEnumTestSuite.test("ResilientEnumWithEmptyCase") {
|
|
let a: [ResilientEnumWithEmptyCase] = getResilientEnumWithEmptyCase()
|
|
|
|
let b: [Int] = a.map {
|
|
switch $0 {
|
|
case .A:
|
|
return 0
|
|
case .B:
|
|
return 1
|
|
case .Empty:
|
|
return 2
|
|
default:
|
|
return -1
|
|
}
|
|
}
|
|
|
|
expectEqual(b, [0, 1, 2])
|
|
}
|
|
|
|
// Methods inside extensions of resilient enums fish out type parameters
|
|
// from metadata -- make sure we can do that
|
|
extension ResilientMultiPayloadGenericEnum {
|
|
public func getTypeParameter() -> T.Type {
|
|
return T.self
|
|
}
|
|
}
|
|
|
|
extension ResilientMultiPayloadGenericEnumFixedSize {
|
|
public func getTypeParameter() -> T.Type {
|
|
return T.self
|
|
}
|
|
}
|
|
|
|
class Base {}
|
|
|
|
ResilientEnumTestSuite.test("ResilientEnumExtension") {
|
|
expectEqual(Base.self, ResilientMultiPayloadGenericEnum<Base>.A.getTypeParameter())
|
|
expectEqual(Base.self, ResilientMultiPayloadGenericEnumFixedSize<Base>.A.getTypeParameter())
|
|
}
|
|
|
|
public class Container {
|
|
private enum Multi {
|
|
case none
|
|
case some(Container)
|
|
case other(ResilientRef)
|
|
}
|
|
private var m: Multi
|
|
var i: Int
|
|
init() {
|
|
m = .none
|
|
i = 0
|
|
switch self.m {
|
|
case .none:
|
|
print("success")
|
|
case .some(_), .other(_):
|
|
assert(false, "noooo!")
|
|
}
|
|
}
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("ResilientPrivateEnumMember") {
|
|
_ = Container()
|
|
}
|
|
|
|
struct Nested {
|
|
var str: String
|
|
var r: ResilientInt
|
|
}
|
|
|
|
enum SingleCase {
|
|
case only(nested: Nested)
|
|
}
|
|
|
|
struct Status {
|
|
let fst: SingleCase
|
|
let snd: Bool
|
|
}
|
|
|
|
func getOptional<T>(_ t: T) -> T? {
|
|
return t
|
|
}
|
|
|
|
func test<T>(_ t: T) {
|
|
let o = getOptional(t)
|
|
if let c = o {
|
|
print("success")
|
|
}
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("ResilientEnumSingleCase") {
|
|
// This used to crash.
|
|
test(Status(fst: .only(nested: Nested(str: "foobar", r: ResilientInt(i: 1))), snd: false))
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("ResilientEnumWithUnavailableCase") {
|
|
let a: [ResilientEnumWithUnavailableCase] = [.available]
|
|
|
|
let b: [Int] = a.map {
|
|
switch $0 {
|
|
case .available:
|
|
return 0
|
|
case .unavailable:
|
|
return 1
|
|
default:
|
|
return -1
|
|
}
|
|
}
|
|
|
|
expectEqual(b, [0])
|
|
}
|
|
|
|
ResilientEnumTestSuite.test("ResilientEnumWithUnavailableCaseAndPayload") {
|
|
let a: [ResilientEnumWithUnavailableCaseAndPayload] =
|
|
[.double(ResilientDouble(d: 42.0))]
|
|
|
|
let b: [Int] = a.map { return $0.intValue }
|
|
|
|
expectEqual(b, [42])
|
|
}
|
|
|
|
runAllTests()
|