Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7a870dc

Browse files
authored
Merge pull request #89053 from aidan-hall/lifedep-partial-apply-result-lifetimes
Lifetimes: Replace deps on partial_apply parameters with 'captures'
2 parents 37c1d74 + f5833c9 commit 7a870dc

5 files changed

Lines changed: 472 additions & 1 deletion

File tree

include/swift/AST/LifetimeDependence.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,21 @@ class LifetimeDependenceInfo {
418418
uncurry(ASTContext &ctx, ArrayRef<LifetimeDependenceInfo> inner,
419419
unsigned numInnerParams, unsigned numOuterParams);
420420

421+
/// Compute the lifetime dependencies for the result of a partial application
422+
/// of a SIL function with the given lifetimes and number of parameters,
423+
/// binding the given number of arguments.
424+
///
425+
/// Dependencies on parameters that are bound by the partial_apply are
426+
/// replaced with 'captures' dependencies.
427+
///
428+
/// Example: binding 1 argument of a 2-argument function.
429+
/// %b = ... : B
430+
/// %f = function_ref @closure : (A, B) -> @lifetime(borrow 1) C
431+
/// %c = partial_apply %f(%b) : (A) -> @lifetime(captures) C
432+
static ArrayRef<LifetimeDependenceInfo>
433+
partialApply(ASTContext &ctx, ArrayRef<LifetimeDependenceInfo> lifetimes,
434+
unsigned numFormalParams, unsigned numBoundParams);
435+
421436
bool operator==(const LifetimeDependenceInfo &other) const {
422437
return this->hasImmortalSpecifier() == other.hasImmortalSpecifier() &&
423438
this->hasCaptures() == other.hasCaptures() &&

lib/AST/LifetimeDependence.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2064,6 +2064,90 @@ ArrayRef<LifetimeDependenceInfo> LifetimeDependenceInfo::uncurry(
20642064
return ctx.AllocateCopy(uncurried);
20652065
}
20662066

2067+
ArrayRef<LifetimeDependenceInfo> LifetimeDependenceInfo::partialApply(
2068+
ASTContext &ctx, ArrayRef<LifetimeDependenceInfo> lifetimes,
2069+
unsigned numFormalParams, unsigned numBoundParams) {
2070+
2071+
if (numBoundParams == 0)
2072+
return lifetimes;
2073+
2074+
ASSERT(numBoundParams <= numFormalParams &&
2075+
"A partial application can only bind as many parameters as the "
2076+
"function has.");
2077+
2078+
// How many parameters the resulting closure will have.
2079+
const unsigned numClosureParams = numFormalParams - numBoundParams;
2080+
2081+
SmallVector<LifetimeDependenceInfo, 2> curried;
2082+
2083+
for (const auto &dep : lifetimes) {
2084+
// Determine the new target index.
2085+
unsigned targetIndex;
2086+
if (dep.getTargetIndex() == numFormalParams) {
2087+
// The target is the result.
2088+
// Its index is the number of parameters.
2089+
targetIndex = numClosureParams;
2090+
} else if (dep.getTargetIndex() >= numClosureParams) {
2091+
// The target is a captured parameter.
2092+
// The resulting closure does not need a lifetime dependence entry for it.
2093+
continue;
2094+
} else {
2095+
// The target is an uncaptured parameter.
2096+
// Its index remains the same.
2097+
targetIndex = dep.getTargetIndex();
2098+
}
2099+
2100+
auto flags = dep.flags;
2101+
2102+
const auto captureBoundParams = [&](IndexSubset *indices) -> IndexSubset * {
2103+
if (!indices)
2104+
return nullptr;
2105+
2106+
ASSERT(indices->getCapacity() <= numFormalParams &&
2107+
"There should be at most 1 index per parameter. SIL functions "
2108+
"cannot have "
2109+
"an implicit self parameter.");
2110+
2111+
auto bits = indices->getBitVector();
2112+
2113+
if (bits.find_last() >= int(numClosureParams)) {
2114+
// One of the lifetime source parameters is bound by the partial_apply.
2115+
// This becomes a captures dependence in the resulting closure.
2116+
flags.setCaptures(true);
2117+
}
2118+
2119+
// Remove the indices of the captured parameters, leaving only those of
2120+
// the closure parameters.
2121+
2122+
if (bits.find_first() >= int(numClosureParams)) {
2123+
// All lifetime sources are captured. The resulting empty list of
2124+
// indices should be represented with a nullptr.
2125+
return nullptr;
2126+
}
2127+
2128+
if (bits.size() > numClosureParams)
2129+
bits.resize(numClosureParams);
2130+
2131+
return IndexSubset::get(ctx, bits);
2132+
};
2133+
2134+
auto inherit = captureBoundParams(dep.getInheritIndices());
2135+
auto scope = captureBoundParams(dep.getScopeIndices());
2136+
auto addressable = captureBoundParams(dep.getAddressableIndices());
2137+
auto conditionallyAddressable =
2138+
captureBoundParams(dep.getConditionallyAddressableIndices());
2139+
2140+
curried.push_back(LifetimeDependenceInfo(inherit, scope, targetIndex,
2141+
addressable,
2142+
conditionallyAddressable, flags));
2143+
}
2144+
2145+
// FIXME: Avoid allocating context memory for every partial apply. Instead,
2146+
// cache a single uniqueLifetimeDependenceInfo array for each combination
2147+
// of FunctionType + numBoundParams.
2148+
return ctx.AllocateCopy(curried);
2149+
}
2150+
20672151
void LifetimeDependenceInfo::dump() const {
20682152
llvm::errs() << "target: " << getTargetIndex() << '\n';
20692153
if (hasImmortalSpecifier()) {

lib/SIL/IR/SILBuilder.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,11 @@ SILType SILBuilder::getPartialApplyResultType(
7272
.intoBuilder()
7373
.withRepresentation(SILFunctionType::Representation::Thick)
7474
.withIsolation(resultIsolation)
75-
.withIsPseudogeneric(false);
75+
.withIsPseudogeneric(false)
76+
.withLifetimeDependencies(LifetimeDependenceInfo::partialApply(
77+
context.getContext()->getASTContext(),
78+
FTI->getLifetimeDependencies(), FTI->getNumParameters(),
79+
argCount));
7680
if (onStack)
7781
extInfoBuilder = extInfoBuilder.withNoEscape();
7882
auto extInfo = extInfoBuilder.build();

test/IRGen/rdar176795176.swift

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// RUN: %target-swift-frontend -emit-ir -O -enable-experimental-feature Lifetimes %s
2+
3+
// REQUIRES: swift_feature_Lifetimes
4+
5+
// Minimal reproducer for IRGen crash: rdar://176795176
6+
7+
// MARK: - NE
8+
9+
public struct NE: ~Escapable {
10+
@_lifetime(immortal)
11+
public init() {}
12+
}
13+
14+
// MARK: - INES
15+
16+
public protocol INES: ~Escapable {
17+
@_lifetime(copy self)
18+
consuming func f(at indices: Range<Int>) -> NE
19+
}
20+
21+
// MARK: - NES
22+
23+
public protocol NES: ~Copyable {
24+
associatedtype Bytes: INES & ~Escapable
25+
}
26+
27+
// MARK: - IRef
28+
29+
public protocol IRef<PR>: ~Copyable {
30+
associatedtype PR: ~Copyable
31+
associatedtype P: BitwiseCopyable
32+
33+
static func a(of p: P) -> UnsafePointer<PR>
34+
}
35+
36+
// MARK: - Ref
37+
38+
@frozen
39+
public struct Ref<L, C, PR>: ~Escapable & ~Copyable
40+
where
41+
L: ~Escapable,
42+
C: ~Copyable & IRef<PR>,
43+
PR: ~Copyable
44+
{
45+
@usableFromInline
46+
let p: C.P
47+
48+
@_lifetime(immortal)
49+
@usableFromInline
50+
init(immortal p: C.P) {
51+
self.p = p
52+
}
53+
}
54+
55+
extension Ref: Copyable
56+
where
57+
L: ~Escapable,
58+
C: Copyable
59+
{}
60+
61+
extension Ref: BitwiseCopyable
62+
where
63+
L: ~Escapable,
64+
C: Copyable
65+
{}
66+
67+
// MARK: - The crashing extension
68+
69+
extension Ref: INES
70+
where
71+
L: ~Escapable,
72+
C: Copyable,
73+
PR: NES & ~Copyable
74+
{
75+
@_lifetime(copy self)
76+
public consuming func f(
77+
at indices: Range<Int>
78+
) -> NE {
79+
let bytes = self.fromPR {
80+
return _overrideL(immortal: $0[bytes: indices])
81+
}
82+
return _overrideL(bytes, copy: self)
83+
}
84+
}
85+
86+
extension Ref
87+
where
88+
L: ~Escapable,
89+
C: ~Copyable,
90+
PR: ~Copyable
91+
{
92+
@_lifetime(copy self)
93+
fileprivate func fromPR<Thrown, Output: ~Escapable>(
94+
body: (borrowing PR) throws(Thrown) -> Output
95+
) throws(Thrown) -> Output
96+
where
97+
Thrown: Error,
98+
Output: ~Copyable
99+
{
100+
return _overrideL(
101+
try body(C.a(of: self.p).pointee),
102+
copy: self
103+
)
104+
}
105+
}
106+
107+
// Subscripts used by f
108+
extension NES where Self: ~Copyable {
109+
subscript(bytes indices: Range<Int>) -> NE {
110+
@_lifetime(borrow self)
111+
get {
112+
NE()
113+
}
114+
}
115+
}
116+
117+
// _overrideL stubs — mimicking stdlib
118+
119+
@_unsafeNonescapableResult
120+
@_lifetime(immortal)
121+
@_alwaysEmitIntoClient
122+
@_transparent
123+
public func _overrideL<T: ~Copyable & ~Escapable>(
124+
immortal value: consuming T
125+
) -> T {
126+
value
127+
}
128+
129+
@_unsafeNonescapableResult
130+
@_lifetime(copy source)
131+
@_alwaysEmitIntoClient
132+
@_transparent
133+
public func _overrideL<T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable>(
134+
_ value: consuming T,
135+
copy source: borrowing U
136+
) -> T {
137+
value
138+
}

0 commit comments

Comments
 (0)