Skip to content

Commit 16d974e

Browse files
committed
SILGen: Mark constant captures for no_consume_or_assign checking instead of may_assign_but_not_consume.
An immutable noncopyable capture borrows the captured value in-place and can't do anything to modify it, and the may_assign_but_not_consume checking behaves badly with some code patterns generated for resilient types when `self` is captured during a deinit. This change allows for more accurate checking and fixes rdar://118427997.
1 parent f126b71 commit 16d974e

File tree

4 files changed

+102
-2
lines changed

4 files changed

+102
-2
lines changed

lib/SILGen/SILGenFunction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,7 @@ void SILGenFunction::emitCaptures(SILLocation loc,
771771
val = B.createMarkUnresolvedNonCopyableValueInst(
772772
loc, val,
773773
MarkUnresolvedNonCopyableValueInst::CheckKind::
774-
AssignableButNotConsumable);
774+
NoConsumeOrAssign);
775775
}
776776
val = emitLoad(loc, val, tl, SGFContext(), IsNotTake).forward(*this);
777777
}

test/Interpreter/Inputs/moveonly_resilient_type.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,36 @@ public struct Resilient: ~Copyable {
2727
return nextValue
2828
}
2929
}
30+
31+
func testCapture(_: () -> Bool) {}
32+
33+
public struct ResilientCapturesInDeinit: ~Copyable {
34+
static var nextValue: Int = 0
35+
36+
private(set) public var value: Int
37+
public init(nonthrowing: ()) {
38+
value = Self.nextValue
39+
Self.nextValue += 1
40+
}
41+
deinit {
42+
testCapture { value >= 0 }
43+
print("resilient capture in deinit \(value)")
44+
}
45+
46+
public init(throwing: Bool) throws {
47+
if throwing {
48+
throw MyError()
49+
}
50+
self = .init(nonthrowing: ())
51+
}
52+
public init(throwingAfterInit: Bool) throws {
53+
self = .init(nonthrowing: ())
54+
if throwingAfterInit {
55+
throw MyError()
56+
}
57+
}
58+
59+
public static func instanceCount() -> Int {
60+
return nextValue
61+
}
62+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -emit-module-path %t -enable-library-evolution -module-name moveonly_resilient_type -parse-as-library %S/Inputs/moveonly_resilient_type.swift -c -o %t/moveonly_resilient_type.o
3+
// RUN: %target-build-swift -enable-library-evolution -module-name moveonly_resilient_type -parse-as-library %S/Inputs/moveonly_resilient_type.swift -c -o %t/moveonly_resilient_type.o
4+
// RUN: %target-build-swift -o %t/a.out -I %t %s %t/moveonly_resilient_type.o
5+
// RUN: %target-run %t/a.out | %FileCheck %s
6+
7+
import moveonly_resilient_type
8+
9+
// CHECK: start
10+
11+
func test1a() throws {
12+
// CHECK-NEXT: resilient capture in deinit 0
13+
_ = ResilientCapturesInDeinit(nonthrowing: ())
14+
}
15+
func test1b() throws {
16+
// CHECK-NEXT: resilient capture in deinit 1
17+
let x = ResilientCapturesInDeinit(nonthrowing: ())
18+
}
19+
func test2a() throws {
20+
// CHECK-NEXT: resilient capture in deinit 2
21+
_ = try ResilientCapturesInDeinit(throwing: false)
22+
}
23+
func test2b() throws {
24+
// CHECK-NEXT: resilient capture in deinit 3
25+
let x = try ResilientCapturesInDeinit(throwing: false)
26+
}
27+
func test3a() throws {
28+
_ = try ResilientCapturesInDeinit(throwing: true)
29+
}
30+
func test3b() throws {
31+
let x = try ResilientCapturesInDeinit(throwing: true)
32+
}
33+
func test4a() throws {
34+
// CHECK-NEXT: resilient capture in deinit 4
35+
_ = try ResilientCapturesInDeinit(throwingAfterInit: false)
36+
}
37+
func test4b() throws {
38+
// CHECK-NEXT: resilient capture in deinit 5
39+
let x = try ResilientCapturesInDeinit(throwingAfterInit: false)
40+
}
41+
func test5a() throws {
42+
// CHECK-NEXT: resilient capture in deinit 6
43+
_ = try ResilientCapturesInDeinit(throwingAfterInit: true)
44+
}
45+
func test5b() throws {
46+
// CHECK-NEXT: resilient capture in deinit 7
47+
let x = try ResilientCapturesInDeinit(throwingAfterInit: true)
48+
}
49+
50+
func main() {
51+
print("start")
52+
53+
_ = try? test1a()
54+
_ = try? test1b()
55+
_ = try? test2a()
56+
_ = try? test2b()
57+
_ = try? test3a()
58+
_ = try? test3b()
59+
_ = try? test4a()
60+
_ = try? test4b()
61+
_ = try? test5a()
62+
_ = try? test5b()
63+
64+
// CHECK-NEXT: total 8
65+
print("total \(ResilientCapturesInDeinit.instanceCount())")
66+
}
67+
main()

test/SILGen/moveonly.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,7 @@ func testGlobalAssign() {
913913
// CHECK: store [[ARG]] to [init] [[PROJECT]]
914914
//
915915
// CHECK: [[FN:%.*]] = function_ref @$s8moveonly49checkMarkUnresolvedNonCopyableValueInstOnCaptured1xyAA2FDVn_tFyyXEfU_ : $@convention(thin) @substituted <τ_0_0> (@guaranteed FD) -> @out τ_0_0 for <()>
916-
// CHECK: [[MARK:%.*]] = mark_unresolved_non_copyable_value [assignable_but_not_consumable] [[PROJECT]]
916+
// CHECK: [[MARK:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[PROJECT]]
917917
// CHECK: [[VALUE:%.*]] = load [copy] [[MARK]]
918918
// CHECK: [[CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[FN]]([[VALUE]])
919919
// CHECK: } // end sil function '$s8moveonly49checkMarkUnresolvedNonCopyableValueInstOnCaptured1xyAA2FDVn_tF'

0 commit comments

Comments
 (0)