Files
swift-mirror/test/Interpreter/consume_operator_kills_copyable_values.swift.gyb
Nate Chandler cae89a9562 [ConsumeObjectChecker] End lifetimes at consumes.
The checker already verifies that no non-destroy consuming users occur
after any `move_value`s corresponding to `consume` operators applied to
a value.  There may, however, be _destroy_ users after it.

Previously, the checker did not shorten the lifetime from those destroys
up to `move_value`s that appear after those `move_value`s.  The result
was that the value's lifetime didn't end at the `consume`.

Here, the checker is fixed to rewrite the lifetimes so that they both
end at `consume`s and also maintain their lexical lifetimes on paths
away from the `consume`s.  This is done by using
`OwnedValueCanonicalization`/`CanonicalizeOSSALifetime`.

Specifically, it passes the `move_value`s that correspond to
source-level `consume`s as the `lexicalLifetimeEnds` to the
canonicalizer.  Typically, the canonicalizer retracts the lexical
lifetime of the value from its destroys.  When these `move_value`s are
specified, however, instead it retracts them from the lifetime boundary
obtained by maximizing the lifetime within its original lifetime while
maintaining the property that the lifetime ends at those `move_value`s.

rdar://113142446
2024-06-03 15:45:32 -07:00

197 lines
5.3 KiB
Swift

// RUN: %target-run-simple-swiftgyb(-Xfrontend -sil-verify-all) | %FileCheck %t/main.swift
// RUN: %target-run-simple-swiftgyb(-O -Xfrontend -sil-verify-all) | %FileCheck %t/main.swift
// This test uses gyb substitute let and var in as the introducers for the
// vardecls that are consumed. This guarantees that the same ordering results
// from checking the consume operator when applied to lets as when applied to
// lets.
// REQUIRES: executable_test
class X {
let number: Int
let function: StaticString
let line: UInt
init(_ number: Int, function: StaticString = #function, line: UInt = #line) {
self.number = number
self.function = function
self.line = line
print("hi - \(function) - \(number) - \(line)")
}
deinit {
print("adios - \(function) - \(number) - \(line)")
}
}
func barrier(_ name: String = "", function: StaticString = #function, line: UInt = #line) {
print("barrier - \(name) - \(function) - \(line)")
}
func endcap(name: String, _ args: [Any], function: StaticString = #function, line: UInt = #line) {
print("\(name) \(function)(", terminator: "")
for (index, arg) in args.enumerated() {
print(arg, terminator: "")
if index != args.count - 1 {
print(", ", terminator: "")
}
}
print(") - \(line)")
}
func begin(_ args: [Any], function: StaticString = #function, line: UInt = #line) {
endcap(name: "begin", args, function: function, line: line)
}
func end(_ args: [Any], function: StaticString = #function, line: UInt = #line) {
endcap(name: "end", args, function: function, line: line)
}
func takeX(_ x: consuming X) {}
% for introducer in ["let", "var"]:
func simple_${introducer}() {
// CHECK-LABEL: begin simple_{{let|var}}
begin([])
do {
// CHECK: hi
${introducer} x = X(0)
// CHECK: adios
takeX(consume x)
// CHECK: barrier
barrier()
}
// CHECK-LABEL: end simple_{{let|var}}
end([])
}
// CHECK-LABEL: begin testLeft_{{let|var}}(_:)(true)
// CHECK: hi
// CHECK: adios
// CHECK: barrier - left
// CHECK: barrier - end
// CHECK-LABEL: end testLeft_{{let|var}}(_:)(true)
// CHECK-LABEL: begin testLeft_{{let|var}}(_:)(false)
// CHECK: hi
// CHECK: barrier - right
// CHECK: adios
// CHECK: barrier - end
// CHECK-LABEL: end testLeft_{{let|var}}(_:)(false)
func testLeft_${introducer}(_ condition: Bool) {
begin([condition])
do {
${introducer} x = X(0)
if condition {
takeX(consume x)
barrier("left")
} else {
barrier("right")
}
barrier("end")
}
end([condition])
}
// CHECK-LABEL: begin testNested1_{{let|var}}(_:_:)(false, false)
// CHECK: hi
// CHECK: barrier - not c1, not c2
// CHECK: barrier - not c1
// CHECK: adios
// CHECK-LABEL: end testNested1_{{let|var}}(_:_:)(false, false)
// CHECK-LABEL: begin testNested1_{{let|var}}(_:_:)(false, true)
// CHECK: hi
// CHECK: barrier - not c1, c2
// CHECK: barrier - not c1
// CHECK: adios
// CHECK-LABEL: end testNested1_{{let|var}}(_:_:)(false, true)
// CHECK-LABEL: begin testNested1_{{let|var}}(_:_:)(true, false)
// CHECK: hi
// CHECK: adios
// CHECK: barrier - c2
// CHECK-LABEL: end testNested1_{{let|var}}(_:_:)(true, false)
// CHECK-LABEL: begin testNested1_{{let|var}}(_:_:)(true, true)
// CHECK: hi
// CHECK: adios
// CHECK: barrier - c2
// CHECK-LABEL: end testNested1_{{let|var}}(_:_:)(true, true)
func testNested1_${introducer}(_ c1: Bool, _ c2: Bool) {
begin([c1, c2])
do {
${introducer} x = X(0)
if c1 {
takeX(consume x)
barrier("c2")
} else {
if c2 {
takeX(x)
barrier("not c1, c2")
} else {
barrier("not c1, not c2")
}
barrier("not c1")
}
}
end([c1, c2])
}
// CHECK-LABEL: begin testNested2_{{let|var}}(_:_:)(false, false)
// CHECK: hi
// CHECK: barrier - not c1, not c2
// CHECK: adios
// CHECK: barrier - not c1
// CHECK-LABEL: end testNested2_{{let|var}}(_:_:)(false, false)
// CHECK-LABEL: begin testNested2_{{let|var}}(_:_:)(false, true)
// CHECK: hi
// CHECK: adios
// CHECK: barrier - not c1, c2
// CHECK: barrier - not c1
// CHECK-LABEL: end testNested2_{{let|var}}(_:_:)(false, true)
// CHECK-LABEL: begin testNested2_{{let|var}}(_:_:)(true, false)
// CHECK: hi
// CHECK: adios
// CHECK: barrier - c1
// CHECK-LABEL: end testNested2_{{let|var}}(_:_:)(true, false)
// CHECK-LABEL: begin testNested2_{{let|var}}(_:_:)(true, true)
// CHECK: hi
// CHECK: adios
// CHECK: barrier - c1
// CHECK-LABEL: end testNested2_{{let|var}}(_:_:)(true, true)
func testNested2_${introducer}(_ c1: Bool, _ c2: Bool) {
begin([c1, c2])
do {
${introducer} x = X(0)
if c1 {
takeX(consume x)
barrier("c1")
} else {
if c2 {
takeX(consume x)
barrier("not c1, c2")
} else {
barrier("not c1, not c2")
}
barrier("not c1")
}
}
end([c1, c2])
}
func main_${introducer}() {
simple_${introducer}()
testLeft_${introducer}(true)
testLeft_${introducer}(false)
testNested1_${introducer}(false, false)
testNested1_${introducer}(false, true)
testNested1_${introducer}(true, false)
testNested1_${introducer}(true, true)
testNested2_${introducer}(false, false)
testNested2_${introducer}(false, true)
testNested2_${introducer}(true, false)
testNested2_${introducer}(true, true)
}
% end
main_let()
main_var()