mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Optional's `init_enum_data_addr` and `inject_enum_addr` instructions are generated in presence of non-loadable Optional values. The compiler used to treat these instructions as inactive, and this resulted in silent run-time issues described in #64223. The patch marks `init_enum_data_addr` as "active" if its Optional operand is also active, and in PullbackCloner we differentiate through it and the related `inject_enum_addr`. However, we only determine this relation in simple cases when both instructions are in the same block. There is no def-use relation between them (both take the same Optional operand), so if there is more than one set of instructions operating on the same Optional, or there is some control flow, we currently bail out. In PullbackCloner, we walk over instructions in reverse order and start from `inject_enum_addr` and its `Optional<Wrapped>.TangentVector` operand. Assuming that is is already initialized, we emit an `unchecked_take_enum_data_addr` and set it as the adjoint buffer of `init_enum_data_addr`. The Optional value is invalidated, and we have to destroy the enum data address later when we reach `init_enum_data_addr`.
86 lines
2.2 KiB
Swift
86 lines
2.2 KiB
Swift
// RUN: %target-swift-emit-sil -verify %s
|
|
|
|
import _Differentiation
|
|
|
|
public enum DiffEnum<Wrapped>: ExpressibleByNilLiteral {
|
|
case none
|
|
case some(Wrapped)
|
|
|
|
@_transparent
|
|
public init(_ some: Wrapped) { self = .some(some) }
|
|
|
|
@_transparent
|
|
public init(nilLiteral: ()) {
|
|
self = .none
|
|
}
|
|
}
|
|
|
|
extension DiffEnum: Differentiable where Wrapped: Differentiable {
|
|
public enum TangentVector: Differentiable, AdditiveArithmetic {
|
|
case none
|
|
case some(Wrapped.TangentVector)
|
|
|
|
public typealias TangentVector = Self
|
|
|
|
public init(_ value: Wrapped.TangentVector?) {
|
|
switch value {
|
|
case .some(let y):
|
|
self = .some(y)
|
|
case .none:
|
|
self = .none
|
|
}
|
|
}
|
|
|
|
public static var zero: Self {
|
|
return Self(.zero)
|
|
}
|
|
|
|
public static func + (lhs: Self, rhs: Self) -> Self {
|
|
switch (lhs, rhs) {
|
|
case let (.some(x), .some(y)): return Self(x + y)
|
|
case let (.some(x), .none): return Self(x)
|
|
case let (.none, .some(y)): return Self(y)
|
|
case (.none, .none): return .none
|
|
}
|
|
}
|
|
|
|
public static func - (lhs: Self, rhs: Self) -> Self {
|
|
switch (lhs, rhs) {
|
|
case let (.some(x), .some(y)): return .some(x - y)
|
|
case let (.some(x), .none): return .some(x)
|
|
case let (.none, .some(y)): return .some(.zero - y)
|
|
case (.none, .none): return .none
|
|
}
|
|
}
|
|
|
|
public mutating func move(by offset: TangentVector) {
|
|
}
|
|
}
|
|
|
|
public mutating func move(by offset: TangentVector) {
|
|
}
|
|
|
|
// expected-error @+2 {{expression is not differentiable}}
|
|
// expected-note @+1 {{differentiating enum values is not yet supported}}
|
|
public func f() -> Wrapped {
|
|
switch (self) {
|
|
case let .some(v): return v
|
|
default: fatalError()
|
|
}
|
|
}
|
|
}
|
|
|
|
// expected-error @+2 {{function is not differentiable}}
|
|
// expected-note @+2 {{when differentiating this function definition}}
|
|
@differentiable(reverse)
|
|
func enumNotSupported<Element>(x: Element) -> Element where Element: Differentiable {
|
|
// expected-note @+1 {{differentiating enum values is not yet supported}}
|
|
let e = DiffEnum<Element>.some(x)
|
|
return e.f()
|
|
}
|
|
|
|
@differentiable(reverse)
|
|
func enumOptional<Element>(x: Element) -> Element? where Element: Differentiable {
|
|
return x
|
|
}
|