Files
swift-mirror/test/AutoDiff/SILOptimizer/optional_pullback_error.swift
Andrew Savonichev 2f7b42ceaf [AutoDiff] Handle init_enum_data_addr and inject_enum_addr for Optional (#68300)
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`.
2023-09-22 01:07:16 -07:00

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
}