Skip to content

Commit efbe78e

Browse files
authored
Merge pull request #18701 from jckarter/capture-list-uniquing-4.2
[4.2] [SILGen] Forming transitive capture lists, need to unique on value not value+flags
2 parents d4b6297 + e928399 commit efbe78e

File tree

3 files changed

+85
-6
lines changed

3 files changed

+85
-6
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: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2063,7 +2063,7 @@ TypeConverter::getLoweredLocalCaptures(AnyFunctionRef fn) {
20632063

20642064
// Recursively collect transitive captures from captured local functions.
20652065
llvm::DenseSet<AnyFunctionRef> visitedFunctions;
2066-
llvm::SetVector<CapturedValue> captures;
2066+
llvm::MapVector<ValueDecl*,CapturedValue> captures;
20672067

20682068
// If there is a capture of 'self' with dynamic 'Self' type, it goes last so
20692069
// that IRGen can pass dynamic 'Self' metadata.
@@ -2143,23 +2143,55 @@ TypeConverter::getLoweredLocalCaptures(AnyFunctionRef fn) {
21432143
goto capture_value;
21442144
}
21452145
}
2146+
2147+
if (!capturedVar->hasStorage())
2148+
continue;
2149+
2150+
// We can always capture the storage in these cases.
2151+
Type captureType = capturedVar->getType();
2152+
if (auto *metatypeType = captureType->getAs<MetatypeType>())
2153+
captureType = metatypeType->getInstanceType();
2154+
2155+
if (auto *selfType = captureType->getAs<DynamicSelfType>()) {
2156+
captureType = selfType->getSelfType();
2157+
2158+
// We're capturing a 'self' value with dynamic 'Self' type;
2159+
// handle it specially.
2160+
if (captureType->getClassOrBoundGenericClass()) {
2161+
if (selfCapture)
2162+
selfCapture = selfCapture->mergeFlags(capture);
2163+
else
2164+
selfCapture = capture;
2165+
continue;
2166+
}
2167+
}
21462168
}
2147-
21482169
capture_value:
21492170
// Collect non-function captures.
2150-
captures.insert(capture);
2171+
ValueDecl *value = capture.getDecl();
2172+
auto existing = captures.find(value);
2173+
if (existing != captures.end()) {
2174+
existing->second = existing->second.mergeFlags(capture);
2175+
} else {
2176+
captures.insert(std::pair<ValueDecl *, CapturedValue>(value, capture));
2177+
}
21512178
}
21522179
};
21532180
collectFunctionCaptures(fn);
21542181

2182+
SmallVector<CapturedValue, 4> resultingCaptures;
2183+
for (auto capturePair : captures) {
2184+
resultingCaptures.push_back(capturePair.second);
2185+
}
2186+
21552187
// If we captured the dynamic 'Self' type and we have a 'self' value also,
21562188
// add it as the final capture. Otherwise, add a fake hidden capture for
21572189
// the dynamic 'Self' metatype.
21582190
if (selfCapture.hasValue()) {
2159-
captures.insert(*selfCapture);
2191+
resultingCaptures.push_back(*selfCapture);
21602192
} else if (capturesDynamicSelf) {
21612193
selfCapture = CapturedValue::getDynamicSelfMetadata();
2162-
captures.insert(*selfCapture);
2194+
resultingCaptures.push_back(*selfCapture);
21632195
}
21642196

21652197
// Cache the uniqued set of transitive captures.
@@ -2168,7 +2200,7 @@ TypeConverter::getLoweredLocalCaptures(AnyFunctionRef fn) {
21682200
auto &cachedCaptures = inserted.first->second;
21692201
cachedCaptures.setGenericParamCaptures(capturesGenericParams);
21702202
cachedCaptures.setDynamicSelfType(capturesDynamicSelf);
2171-
cachedCaptures.setCaptures(Context.AllocateCopy(captures));
2203+
cachedCaptures.setCaptures(Context.AllocateCopy(resultingCaptures));
21722204

21732205
return cachedCaptures;
21742206
}

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-frontend -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)