Skip to content

Commit 5a1b0e7

Browse files
authored
Merge pull request #18514 from gregomni/8398
[SILGen] Forming transitive capture lists, need to unique on value not value+flags
2 parents d52dad3 + 4261fc7 commit 5a1b0e7

File tree

3 files changed

+69
-9
lines changed

3 files changed

+69
-9
lines changed

include/swift/AST/CaptureInfo.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ class CapturedValue {
6464

6565
bool isDynamicSelfMetadata() const { return !Value.getPointer(); }
6666

67+
CapturedValue mergeFlags(CapturedValue cv) {
68+
assert(Value.getPointer() == cv.Value.getPointer() &&
69+
"merging flags on two different value decls");
70+
return CapturedValue(Value.getPointer(), getFlags() & cv.getFlags());
71+
}
72+
6773
ValueDecl *getDecl() const {
6874
assert(Value.getPointer() && "dynamic Self metadata capture does not "
6975
"have a value");

lib/SIL/TypeLowering.cpp

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2038,7 +2038,7 @@ TypeConverter::getLoweredLocalCaptures(AnyFunctionRef fn) {
20382038

20392039
// Recursively collect transitive captures from captured local functions.
20402040
llvm::DenseSet<AnyFunctionRef> visitedFunctions;
2041-
llvm::SetVector<CapturedValue> captures;
2041+
llvm::MapVector<ValueDecl*,CapturedValue> captures;
20422042

20432043
// If there is a capture of 'self' with dynamic 'Self' type, it goes last so
20442044
// that IRGen can pass dynamic 'Self' metadata.
@@ -2139,30 +2139,43 @@ TypeConverter::getLoweredLocalCaptures(AnyFunctionRef fn) {
21392139

21402140
// We're capturing a 'self' value with dynamic 'Self' type;
21412141
// handle it specially.
2142-
if (!selfCapture &&
2143-
captureType->getClassOrBoundGenericClass()) {
2144-
selfCapture = capture;
2142+
if (captureType->getClassOrBoundGenericClass()) {
2143+
if (selfCapture)
2144+
selfCapture = selfCapture->mergeFlags(capture);
2145+
else
2146+
selfCapture = capture;
21452147
continue;
21462148
}
21472149
}
21482150

21492151
// Fall through to capture the storage.
21502152
}
2151-
2153+
21522154
// Collect non-function captures.
2153-
captures.insert(capture);
2155+
ValueDecl *value = capture.getDecl();
2156+
auto existing = captures.find(value);
2157+
if (existing != captures.end()) {
2158+
existing->second = existing->second.mergeFlags(capture);
2159+
} else {
2160+
captures.insert(std::pair<ValueDecl *, CapturedValue>(value, capture));
2161+
}
21542162
}
21552163
};
21562164
collectFunctionCaptures(fn);
21572165

2166+
SmallVector<CapturedValue, 4> resultingCaptures;
2167+
for (auto capturePair : captures) {
2168+
resultingCaptures.push_back(capturePair.second);
2169+
}
2170+
21582171
// If we captured the dynamic 'Self' type and we have a 'self' value also,
21592172
// add it as the final capture. Otherwise, add a fake hidden capture for
21602173
// the dynamic 'Self' metatype.
21612174
if (selfCapture.hasValue()) {
2162-
captures.insert(*selfCapture);
2175+
resultingCaptures.push_back(*selfCapture);
21632176
} else if (capturesDynamicSelf) {
21642177
selfCapture = CapturedValue::getDynamicSelfMetadata();
2165-
captures.insert(*selfCapture);
2178+
resultingCaptures.push_back(*selfCapture);
21662179
}
21672180

21682181
// Cache the uniqued set of transitive captures.
@@ -2171,7 +2184,7 @@ TypeConverter::getLoweredLocalCaptures(AnyFunctionRef fn) {
21712184
auto &cachedCaptures = inserted.first->second;
21722185
cachedCaptures.setGenericParamCaptures(capturesGenericParams);
21732186
cachedCaptures.setDynamicSelfType(capturesDynamicSelf);
2174-
cachedCaptures.setCaptures(Context.AllocateCopy(captures));
2187+
cachedCaptures.setCaptures(Context.AllocateCopy(resultingCaptures));
21752188

21762189
return cachedCaptures;
21772190
}

test/SILGen/capture-transitive.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: %target-swift-emit-silgen -enable-sil-ownership %s | %FileCheck %s
2+
// SR-8398
3+
func fibonacci(_ n: Int) -> Int {
4+
var cache: [Int: Int] = [:]
5+
6+
func recursive(_ m: Int) -> Int {
7+
return cache[m] ?? {
8+
// Make sure cache is only captured once in the closure
9+
// CHECK: implicit closure #1 in recursive #1
10+
// CHECK-LABEL: sil private [transparent] @{{.*}}9fibonacci{{.*}}9recursive{{.*}} : $@convention(thin) (Int, @guaranteed { var Dictionary<Int, Int> }) -> (Int, @error Error)
11+
// CHECK: closure #1 in implicit closure #1 in recursive #1
12+
// CHECK-LABEL: sil private @{{.*}}9fibonacci{{.*}}9recursive{{.*}} : $@convention(thin) (Int, @guaranteed { var Dictionary<Int, Int> }) -> Int
13+
let output = m < 2 ? m : recursive(m - 1) + recursive(m - 2)
14+
cache[m] = output
15+
return output
16+
}()
17+
}
18+
return recursive(n)
19+
}
20+
21+
class C {
22+
required init() {}
23+
func f() {}
24+
// Make sure that we capture dynamic self type if an explicit self isn't guaranteed
25+
func returnsSelf() -> Self {
26+
return { self.f(); return .init() }()
27+
// CHECK-LABEL: sil private @{{.*}}returnsSelf{{.*}} : $@convention(thin) (@guaranteed C) -> @owned C
28+
}
29+
30+
func returnsSelf1() -> Self {
31+
return { [weak self] in self?.f(); return .init() }()
32+
// CHECK-LABEL: sil private @{{.*}}returnsSelf{{.*}} : $@convention(thin) (@guaranteed { var @sil_weak Optional<C> }, @thick @dynamic_self C.Type) -> @owned C
33+
}
34+
35+
func returnsSelf2() -> Self {
36+
return { [unowned self] in self.f(); return .init() }()
37+
// CHECK-LABEL: sil private @{{.*}}returnsSelf{{.*}} : $@convention(thin) (@guaranteed @sil_unowned C, @thick @dynamic_self C.Type) -> @owned C
38+
}
39+
}
40+
41+

0 commit comments

Comments
 (0)