Skip to content

Commit d312589

Browse files
Merge pull request #66585 from nate-chandler/rdar99681073
[MoveOnlyAddressChecker] Maximize lifetimes.
2 parents 91ee824 + 2bfa723 commit d312589

9 files changed

+1861
-53
lines changed

lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp

+334-1
Large diffs are not rendered by default.

lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "MoveOnlyDiagnostics.h"
1616

17+
#include "swift/AST/Decl.h"
1718
#include "swift/AST/DiagnosticsSIL.h"
1819
#include "swift/AST/Stmt.h"
1920
#include "swift/Basic/Defer.h"
@@ -226,6 +227,12 @@ void DiagnosticEmitter::emitMissingConsumeInDiscardingContext(
226227
return true;
227228

228229
case SILLocation::RegularKind: {
230+
Decl *decl = loc.getAsASTNode<Decl>();
231+
if (decl && isa<AbstractFunctionDecl>(decl)) {
232+
// Having the function itself as a location results in a location at the
233+
// first line of the function. Find another location.
234+
return false;
235+
}
229236
Stmt *stmt = loc.getAsASTNode<Stmt>();
230237
if (!stmt)
231238
return true; // For non-statements, assume it is exiting the func.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
// RUN: %target-run-simple-swift(-Xfrontend -sil-verify-all) | %FileCheck %s
2+
// RUN: %target-run-simple-swift(-O -Xfrontend -sil-verify-all) | %FileCheck %s
3+
4+
struct S : ~Copyable {
5+
let s: String
6+
init(_ s: String) { self.s = s }
7+
deinit {
8+
print("destroying \(s)")
9+
}
10+
}
11+
struct S2 : ~Copyable {
12+
var s1: S
13+
var s2: S
14+
init(_ s: String) {
15+
self.s1 = S("\(s).s1")
16+
self.s2 = S("\(s).s2")
17+
}
18+
}
19+
struct S3 : ~Copyable {
20+
var s1: S
21+
var s2: S
22+
var s3: S
23+
init(_ s: String) {
24+
self.s1 = S("\(s).s1")
25+
self.s2 = S("\(s).s2")
26+
self.s3 = S("\(s).s3")
27+
}
28+
}
29+
30+
func consumeVal(_ s: consuming S) {}
31+
func consumeVal(_ s: consuming S2) {}
32+
func borrowVal(_ s: borrowing S) {}
33+
func borrowVal(_ s: borrowing S2) {}
34+
35+
func marker(_ s: String) {
36+
print("\(#function): \(s)")
37+
}
38+
39+
// Simple test that makes sure that we still after we consume have the lifetime
40+
// of s be completely consumed by consumeVal.
41+
// CHECK: destroying simpleTestVar().first.s1
42+
// CHECK: destroying simpleTestVar().first.s2
43+
// CHECK: destroying simpleTestVar().second.s1
44+
// CHECK: destroying simpleTestVar().second.s2
45+
// CHECK: marker(_:): simpleTestVar().1
46+
@_silgen_name("simpleTestVar")
47+
func simpleTestVar() {
48+
var s = S2("\(#function).first")
49+
s = S2("\(#function).second")
50+
consumeVal(s) // Lifetime of s should end here before end of scope.
51+
marker("\(#function).1")
52+
}
53+
54+
// Simple test that proves that we can maximize lifetimes in a field sensitive
55+
// manner. Since we only consume s.s1, s.s2's lifetime should still be maximized
56+
// and be at end of scope.
57+
// CHECK: destroying simpleTestVar2().first.s1
58+
// CHECK: destroying simpleTestVar2().first.s2
59+
// CHECK: destroying simpleTestVar2().second.s1
60+
// CHECK: marker(_:): simpleTestVar2().1
61+
// CHECK: destroying simpleTestVar2().second.s2
62+
func simpleTestVar2() {
63+
var s = S2("\(#function).first")
64+
s = S2("\(#function).second")
65+
consumeVal(s.s1) // Lifetime of s1 should end here.
66+
marker("\(#function).1")
67+
// Lifetime of s2 should end at end of scope after marker.
68+
}
69+
70+
// In this case, we consume all of s by consuming s.s1 and s.s2 separately, so
71+
// all lifetimes should be done before marker.
72+
// CHECK: destroying simpleTestVar3().first.s1
73+
// CHECK: destroying simpleTestVar3().first.s2
74+
// CHECK: destroying simpleTestVar3().second.s1
75+
// CHECK: destroying simpleTestVar3().second.s2
76+
// CHECK: marker(_:): simpleTestVar3().1
77+
func simpleTestVar3() {
78+
var s = S2("\(#function).first")
79+
s = S2("\(#function).second")
80+
consumeVal(s.s1)
81+
consumeVal(s.s2)
82+
marker("\(#function).1") // Lifetimes should end before marker.
83+
}
84+
85+
// In this case, we completely consume s and then reinitialize s implying we
86+
// need to deal with two disjoint lifetimes. The second lifetime of s should end
87+
// after marker.
88+
// CHECK: destroying simpleTestVar3a().first.s1
89+
// CHECK: destroying simpleTestVar3a().first.s2
90+
// CHECK: destroying simpleTestVar3a().second.s1
91+
// CHECK: destroying simpleTestVar3a().second.s2
92+
// CHECK: marker(_:): simpleTestVar3a().1
93+
// CHECK: marker(_:): simpleTestVar3a().2
94+
// CHECK: destroying simpleTestVar3a().third.s1
95+
// CHECK: destroying simpleTestVar3a().third.s2
96+
func simpleTestVar3a() {
97+
var s = S2("\(#function).first")
98+
s = S2("\(#function).second")
99+
consumeVal(s.s1)
100+
consumeVal(s.s2)
101+
102+
marker("\(#function).1")
103+
104+
s = S2("\(#function).third")
105+
marker("\(#function).2")
106+
}
107+
108+
// In this case, we have another borrowVal of s.s2. That should still let s.s2's
109+
// lifetime end after marker.
110+
// CHECK: destroying simpleTestVar3b().first.s1
111+
// CHECK: destroying simpleTestVar3b().first.s2
112+
// CHECK: destroying simpleTestVar3b().second.s1
113+
// CHECK: marker(_:): simpleTestVar3b().1
114+
// CHECK: destroying simpleTestVar3b().second.s2
115+
func simpleTestVar3b() {
116+
var s = S2("\(#function).first")
117+
s = S2("\(#function).second")
118+
consumeVal(s.s1)
119+
borrowVal(s.s2)
120+
marker("\(#function).1") // s2 should end its lifetime after marker.
121+
}
122+
123+
// In this case, we are testing reinitialization and making sure that we can
124+
// handle two initializations properly. We also are testing conditional merge
125+
// logic. Since in both cases below s is completely consumed in b, s's lifetime
126+
// would end at marker.
127+
128+
// CHECK: destroying simpleTestVar4(_:_:)[false, false)].first.s1
129+
// CHECK: destroying simpleTestVar4(_:_:)[false, false)].first.s2
130+
// CHECK: marker(_:): simpleTestVar4(_:_:)[false, false)].1
131+
// CHECK: destroying simpleTestVar4(_:_:)[false, false)].second.s1
132+
// CHECK: destroying simpleTestVar4(_:_:)[false, false)].second.s2
133+
// CHECK: destroying simpleTestVar4(_:_:)[false, false)].third.s1
134+
// CHECK: destroying simpleTestVar4(_:_:)[false, false)].third.s2
135+
// CHECK: marker(_:): simpleTestVar4(_:_:)[false, false)].2
136+
137+
// CHECK: destroying simpleTestVar4(_:_:)[false, true)].first.s1
138+
// CHECK: destroying simpleTestVar4(_:_:)[false, true)].first.s2
139+
// CHECK: marker(_:): simpleTestVar4(_:_:)[false, true)].1
140+
// CHECK: destroying simpleTestVar4(_:_:)[false, true)].second.s1
141+
// CHECK: destroying simpleTestVar4(_:_:)[false, true)].second.s2
142+
// CHECK: destroying simpleTestVar4(_:_:)[false, true)].third.s1
143+
// CHECK: destroying simpleTestVar4(_:_:)[false, true)].third.s2
144+
145+
// CHECK: destroying simpleTestVar4(_:_:)[true, false)].first.s1
146+
// CHECK: destroying simpleTestVar4(_:_:)[true, false)].first.s2
147+
// CHECK: destroying simpleTestVar4(_:_:)[true, false)].second.s1
148+
// CHECK: destroying simpleTestVar4(_:_:)[true, false)].second.s2
149+
// CHECK: destroying simpleTestVar4(_:_:)[true, false)].third.s1
150+
// CHECK: destroying simpleTestVar4(_:_:)[true, false)].third.s2
151+
// CHECK: marker(_:): simpleTestVar4(_:_:)[true, false)].2
152+
153+
// CHECK: destroying simpleTestVar4(_:_:)[true, true)].first.s1
154+
// CHECK: destroying simpleTestVar4(_:_:)[true, true)].first.s2
155+
// CHECK: destroying simpleTestVar4(_:_:)[true, true)].second.s1
156+
// CHECK: destroying simpleTestVar4(_:_:)[true, true)].second.s2
157+
// CHECK: destroying simpleTestVar4(_:_:)[true, true)].third.s1
158+
// CHECK: destroying simpleTestVar4(_:_:)[true, true)].third.s2
159+
func simpleTestVar4(_ b1: Bool, _ b2: Bool) {
160+
var s = S2("\(#function)[\(b1), \(b2))].first")
161+
s = S2("\(#function)[\(b1), \(b2))].second")
162+
163+
if b1 {
164+
consumeVal(s)
165+
} else {
166+
marker("\(#function)[\(b1), \(b2))].1")
167+
// S's lifetime should end after marker in this block.
168+
}
169+
170+
s = S2("\(#function)[\(b1), \(b2))].third")
171+
172+
if b2 {
173+
consumeVal(s)
174+
} else {
175+
marker("\(#function)[\(b1), \(b2))].2")
176+
// S's 2nd lifetime should end after marker in this block.
177+
}
178+
}
179+
180+
// This test is similar to the previous, except we are consuming different
181+
// values along the if/else branch that completely covers the value. As a result
182+
// of this, we need to end the lifetime of s in the branches.
183+
// CHECK: destroying simpleTestVar6(_:)[false].first.s1
184+
// CHECK: destroying simpleTestVar6(_:)[false].first.s2
185+
// CHECK: destroying simpleTestVar6(_:)[false].second.s2
186+
// CHECK: marker(_:): simpleTestVar6(_:)[false].2
187+
// CHECK: destroying simpleTestVar6(_:)[false].second.s1
188+
// CHECK: destroying simpleTestVar6(_:)[false].third.s1
189+
// CHECK: destroying simpleTestVar6(_:)[false].third.s2
190+
191+
// CHECK: destroying simpleTestVar6(_:)[true].first.s1
192+
// CHECK: destroying simpleTestVar6(_:)[true].first.s2
193+
// CHECK: destroying simpleTestVar6(_:)[true].second.s1
194+
// CHECK: marker(_:): simpleTestVar6(_:)[true].1
195+
// CHECK: destroying simpleTestVar6(_:)[true].second.s2
196+
// CHECK: destroying simpleTestVar6(_:)[true].third.s1
197+
// CHECK: destroying simpleTestVar6(_:)[true].third.s2
198+
func simpleTestVar6(_ b: Bool) {
199+
var s = S2("\(#function)[\(b)].first")
200+
s = S2("\(#function)[\(b)].second")
201+
202+
if b {
203+
consumeVal(s.s1) // end of s.s1's lifetime.
204+
marker("\(#function)[\(b)].1")
205+
// s.s2 should end here.
206+
} else {
207+
consumeVal(s.s2) // end of s.s2's lifetime
208+
marker("\(#function)[\(b)].2")
209+
// end of s.s1's lifetime should end after marker.
210+
}
211+
212+
s = S2("\(#function)[\(b)].third")
213+
}
214+
215+
// In this case, we are using S3 implying we have three fields. So despite the
216+
// fact that we are deleting these two values in the if-else branches, s3's
217+
// lifetime needs to end at end of scope.
218+
// CHECK: destroying simpleTestVar6a(_:)[false].first.s1
219+
// CHECK: destroying simpleTestVar6a(_:)[false].first.s2
220+
// CHECK: destroying simpleTestVar6a(_:)[false].first.s3
221+
// CHECK: destroying simpleTestVar6a(_:)[false].second.s2
222+
// CHECK: marker(_:): simpleTestVar6a(_:)[false].2
223+
// CHECK: destroying simpleTestVar6a(_:)[false].second.s1
224+
// CHECK: marker(_:): simpleTestVar6a(_:)[false].3
225+
// CHECK: destroying simpleTestVar6a(_:)[false].second.s3
226+
227+
// CHECK: destroying simpleTestVar6a(_:)[true].first.s1
228+
// CHECK: destroying simpleTestVar6a(_:)[true].first.s2
229+
// CHECK: destroying simpleTestVar6a(_:)[true].first.s3
230+
// CHECK: destroying simpleTestVar6a(_:)[true].second.s1
231+
// CHECK: marker(_:): simpleTestVar6a(_:)[true].1
232+
// CHECK: destroying simpleTestVar6a(_:)[true].second.s2
233+
// CHECK: marker(_:): simpleTestVar6a(_:)[true].3
234+
// CHECK: destroying simpleTestVar6a(_:)[true].second.s3
235+
func simpleTestVar6a(_ b: Bool) {
236+
var s = S3("\(#function)[\(b)].first")
237+
s = S3("\(#function)[\(b)].second")
238+
239+
if b {
240+
consumeVal(s.s1) // end of s.s1's lifetime.
241+
marker("\(#function)[\(b)].1")
242+
// s.s2 should end here.
243+
} else {
244+
consumeVal(s.s2) // end of s.s2's lifetime
245+
marker("\(#function)[\(b)].2")
246+
// end of s.s1's lifetime should end after marker.
247+
}
248+
249+
marker("\(#function)[\(b)].3")
250+
// s.s3's lifetime should end here.
251+
}
252+
253+
// In this case, we are using S3, but we are consuming two disjoint parts of S
254+
// in the if statement so we cover again completely.
255+
// CHECK: destroying simpleTestVar6b(_:)[false].first.s1
256+
// CHECK: destroying simpleTestVar6b(_:)[false].first.s2
257+
// CHECK: destroying simpleTestVar6b(_:)[false].first.s3
258+
// CHECK: destroying simpleTestVar6b(_:)[false].second.s2
259+
// CHECK: marker(_:): simpleTestVar6b(_:)[false].2
260+
// CHECK: destroying simpleTestVar6b(_:)[false].second.s3
261+
// CHECK: destroying simpleTestVar6b(_:)[false].second.s1
262+
// CHECK: marker(_:): simpleTestVar6b(_:)[false].3
263+
264+
// CHECK: destroying simpleTestVar6b(_:)[true].first.s1
265+
// CHECK: destroying simpleTestVar6b(_:)[true].first.s2
266+
// CHECK: destroying simpleTestVar6b(_:)[true].first.s3
267+
// CHECK: destroying simpleTestVar6b(_:)[true].second.s1
268+
// CHECK: destroying simpleTestVar6b(_:)[true].second.s3
269+
// CHECK: marker(_:): simpleTestVar6b(_:)[true].1
270+
// CHECK: destroying simpleTestVar6b(_:)[true].second.s2
271+
// CHECK: marker(_:): simpleTestVar6b(_:)[true].3
272+
func simpleTestVar6b(_ b: Bool) {
273+
var s = S3("\(#function)[\(b)].first")
274+
s = S3("\(#function)[\(b)].second")
275+
276+
if b {
277+
consumeVal(s.s1) // end of s.s1's lifetime.
278+
consumeVal(s.s3) // end of s.s3's lifetime
279+
marker("\(#function)[\(b)].1")
280+
// s.s2 should end here.
281+
} else {
282+
consumeVal(s.s2) // end of s.s2's lifetime
283+
marker("\(#function)[\(b)].2")
284+
// end of s.s1's lifetime should end after marker.
285+
// end of s.s3's lifetime should end after marker.
286+
}
287+
288+
marker("\(#function)[\(b)].3")
289+
}
290+
291+
292+
simpleTestVar()
293+
simpleTestVar2()
294+
simpleTestVar3()
295+
simpleTestVar3a()
296+
simpleTestVar3b()
297+
simpleTestVar4(false, false)
298+
simpleTestVar4(false, true)
299+
simpleTestVar4(true, false)
300+
simpleTestVar4(true, true)
301+
simpleTestVar6(false)
302+
simpleTestVar6(true)
303+
simpleTestVar6a(false)
304+
simpleTestVar6a(true)
305+
simpleTestVar6b(false)
306+
simpleTestVar6b(true)
307+
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: %target-run-simple-swift(-Xfrontend -sil-verify-all) | %FileCheck %s
2+
// RUN: %target-run-simple-swift(-O -Xfrontend -sil-verify-all) | %FileCheck %s
3+
4+
// REQUIRES: executable_test
5+
struct Alice: ~Copyable {
6+
var age: Int
7+
8+
init(age: Int) {
9+
print("INIT");
10+
self.age = age
11+
}
12+
13+
deinit { print("DEINIT") }
14+
}
15+
16+
func eatMe(_ alice: consuming Alice) {
17+
print(" start")
18+
print(" age:", alice.age)
19+
print(" end")
20+
}
21+
22+
func doit() {
23+
let alice = Alice(age: 10)
24+
eatMe(alice)
25+
}
26+
27+
doit()
28+
29+
// CHECK: INIT
30+
// CHECK: start
31+
// CHECK: age: 10
32+
// CHECK: end
33+
// CHECK: DEINIT

0 commit comments

Comments
 (0)