Skip to content

Commit 27e881d

Browse files
authored
[AST] Shrink CaptureInfo down to a PointerIntPair (swiftlang#27261)
If there are any actual captures, a CaptureInfoStorage will be allocated in the ASTContext, matching the previous behavior of allocating the array of captures there. No functionality change. I /tried/ to change the functionality to assert everywhere capture info was used but hadn't explicitly been computed, but it turns out we do that /all over the place/ for any declaration that's been synthesized, imported, or deserialized. I left in some groundwork for someone to make this more explicit (or requestify it) in the future.
1 parent 91a5aba commit 27e881d

File tree

4 files changed

+137
-76
lines changed

4 files changed

+137
-76
lines changed

include/swift/AST/CaptureInfo.h

Lines changed: 68 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414
#define SWIFT_AST_CAPTURE_INFO_H
1515

1616
#include "swift/Basic/LLVM.h"
17+
#include "swift/Basic/OptionSet.h"
1718
#include "swift/Basic/SourceLoc.h"
1819
#include "swift/AST/TypeAlignments.h"
1920
#include "llvm/ADT/ArrayRef.h"
2021
#include "llvm/ADT/PointerIntPair.h"
2122
#include "llvm/ADT/PointerUnion.h"
23+
#include "llvm/Support/TrailingObjects.h"
2224
#include <vector>
2325

2426
namespace swift {
@@ -114,31 +116,63 @@ class DynamicSelfType;
114116

115117
/// Stores information about captured variables.
116118
class CaptureInfo {
117-
const CapturedValue *Captures;
118-
DynamicSelfType *DynamicSelf;
119-
OpaqueValueExpr *OpaqueValue;
120-
unsigned Count = 0;
121-
bool GenericParamCaptures : 1;
122-
bool Computed : 1;
119+
class CaptureInfoStorage final
120+
: public llvm::TrailingObjects<CaptureInfoStorage, CapturedValue> {
121+
122+
DynamicSelfType *DynamicSelf;
123+
OpaqueValueExpr *OpaqueValue;
124+
unsigned Count;
125+
public:
126+
explicit CaptureInfoStorage(unsigned count, DynamicSelfType *dynamicSelf,
127+
OpaqueValueExpr *opaqueValue)
128+
: DynamicSelf(dynamicSelf), OpaqueValue(opaqueValue), Count(count) { }
129+
130+
ArrayRef<CapturedValue> getCaptures() const {
131+
return llvm::makeArrayRef(this->getTrailingObjects<CapturedValue>(),
132+
Count);
133+
}
134+
135+
DynamicSelfType *getDynamicSelfType() const {
136+
return DynamicSelf;
137+
}
138+
139+
OpaqueValueExpr *getOpaqueValue() const {
140+
return OpaqueValue;
141+
}
142+
};
143+
144+
enum class Flags : unsigned {
145+
HasGenericParamCaptures = 1 << 0
146+
};
147+
148+
llvm::PointerIntPair<const CaptureInfoStorage *, 2, OptionSet<Flags>>
149+
StorageAndFlags;
123150

124151
public:
125-
CaptureInfo()
126-
: Captures(nullptr), DynamicSelf(nullptr), OpaqueValue(nullptr), Count(0),
127-
GenericParamCaptures(0), Computed(0) { }
152+
/// The default-constructed CaptureInfo is "not yet computed".
153+
CaptureInfo() = default;
154+
CaptureInfo(ASTContext &ctx, ArrayRef<CapturedValue> captures,
155+
DynamicSelfType *dynamicSelf, OpaqueValueExpr *opaqueValue,
156+
bool genericParamCaptures);
128157

129-
bool hasBeenComputed() const { return Computed; }
158+
/// A CaptureInfo representing no captures at all.
159+
static CaptureInfo empty();
160+
161+
bool hasBeenComputed() const {
162+
return StorageAndFlags.getPointer();
163+
}
130164

131165
bool isTrivial() const {
132-
return Count == 0 && !GenericParamCaptures && !DynamicSelf && !OpaqueValue;
166+
return getCaptures().empty() && !hasGenericParamCaptures() &&
167+
!hasDynamicSelfCapture() && !hasOpaqueValueCapture();
133168
}
134169

135170
ArrayRef<CapturedValue> getCaptures() const {
136-
return llvm::makeArrayRef(Captures, Count);
137-
}
138-
void setCaptures(ArrayRef<CapturedValue> C) {
139-
Captures = C.data();
140-
Computed = true;
141-
Count = C.size();
171+
// FIXME: Ideally, everywhere that synthesizes a function should include
172+
// its capture info.
173+
if (!hasBeenComputed())
174+
return None;
175+
return StorageAndFlags.getPointer()->getCaptures();
142176
}
143177

144178
/// Return a filtered list of the captures for this function,
@@ -152,37 +186,37 @@ class CaptureInfo {
152186

153187
/// \returns true if the function captures any generic type parameters.
154188
bool hasGenericParamCaptures() const {
155-
return GenericParamCaptures;
156-
}
157-
158-
void setGenericParamCaptures(bool genericParamCaptures) {
159-
GenericParamCaptures = genericParamCaptures;
189+
// FIXME: Ideally, everywhere that synthesizes a function should include
190+
// its capture info.
191+
if (!hasBeenComputed())
192+
return false;
193+
return StorageAndFlags.getInt().contains(Flags::HasGenericParamCaptures);
160194
}
161195

162196
/// \returns true if the function captures the dynamic Self type.
163197
bool hasDynamicSelfCapture() const {
164-
return DynamicSelf != nullptr;
198+
return getDynamicSelfType() != nullptr;
165199
}
166200

167201
/// \returns the captured dynamic Self type, if any.
168202
DynamicSelfType *getDynamicSelfType() const {
169-
return DynamicSelf;
170-
}
171-
172-
void setDynamicSelfType(DynamicSelfType *dynamicSelf) {
173-
DynamicSelf = dynamicSelf;
203+
// FIXME: Ideally, everywhere that synthesizes a function should include
204+
// its capture info.
205+
if (!hasBeenComputed())
206+
return nullptr;
207+
return StorageAndFlags.getPointer()->getDynamicSelfType();
174208
}
175209

176210
bool hasOpaqueValueCapture() const {
177-
return OpaqueValue != nullptr;
211+
return getOpaqueValue() != nullptr;
178212
}
179213

180214
OpaqueValueExpr *getOpaqueValue() const {
181-
return OpaqueValue;
182-
}
183-
184-
void setOpaqueValue(OpaqueValueExpr *OVE) {
185-
OpaqueValue = OVE;
215+
// FIXME: Ideally, everywhere that synthesizes a function should include
216+
// its capture info.
217+
if (!hasBeenComputed())
218+
return nullptr;
219+
return StorageAndFlags.getPointer()->getOpaqueValue();
186220
}
187221

188222
void dump() const;

lib/AST/CaptureInfo.cpp

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,50 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "swift/AST/CaptureInfo.h"
14+
#include "swift/AST/ASTContext.h"
1415
#include "swift/AST/Decl.h"
1516
#include "llvm/Support/raw_ostream.h"
1617

1718
using namespace swift;
1819

20+
CaptureInfo::CaptureInfo(ASTContext &ctx, ArrayRef<CapturedValue> captures,
21+
DynamicSelfType *dynamicSelf,
22+
OpaqueValueExpr *opaqueValue,
23+
bool genericParamCaptures) {
24+
static_assert(IsTriviallyDestructible<CapturedValue>::value,
25+
"Capture info is alloc'd on the ASTContext and not destroyed");
26+
static_assert(IsTriviallyDestructible<CaptureInfo::CaptureInfoStorage>::value,
27+
"Capture info is alloc'd on the ASTContext and not destroyed");
28+
29+
OptionSet<Flags> flags;
30+
if (genericParamCaptures)
31+
flags |= Flags::HasGenericParamCaptures;
32+
33+
if (captures.empty() && !dynamicSelf && !opaqueValue) {
34+
*this = CaptureInfo::empty();
35+
StorageAndFlags.setInt(flags);
36+
return;
37+
}
38+
39+
size_t storageToAlloc =
40+
CaptureInfoStorage::totalSizeToAlloc<CapturedValue>(captures.size());
41+
void *storageBuf = ctx.Allocate(storageToAlloc, alignof(CaptureInfoStorage));
42+
auto *storage = new (storageBuf) CaptureInfoStorage(captures.size(),
43+
dynamicSelf,
44+
opaqueValue);
45+
StorageAndFlags.setPointerAndInt(storage, flags);
46+
std::uninitialized_copy(captures.begin(), captures.end(),
47+
storage->getTrailingObjects<CapturedValue>());
48+
}
49+
50+
CaptureInfo CaptureInfo::empty() {
51+
static const CaptureInfoStorage empty{0, /*dynamicSelf*/nullptr,
52+
/*opaqueValue*/nullptr};
53+
CaptureInfo result;
54+
result.StorageAndFlags.setPointer(&empty);
55+
return result;
56+
}
57+
1958
bool CaptureInfo::hasLocalCaptures() const {
2059
for (auto capture : getCaptures())
2160
if (capture.getDecl()->getDeclContext()->isLocalContext())
@@ -28,7 +67,7 @@ void CaptureInfo::
2867
getLocalCaptures(SmallVectorImpl<CapturedValue> &Result) const {
2968
if (!hasLocalCaptures()) return;
3069

31-
Result.reserve(Count);
70+
Result.reserve(getCaptures().size());
3271

3372
// Filter out global variables.
3473
for (auto capture : getCaptures()) {

lib/SIL/TypeLowering.cpp

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2310,15 +2310,12 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) {
23102310
}
23112311

23122312
// Cache the uniqued set of transitive captures.
2313-
auto inserted = LoweredCaptures.insert({fn, CaptureInfo()});
2313+
CaptureInfo info{Context, resultingCaptures, capturesDynamicSelf,
2314+
capturesOpaqueValue, capturesGenericParams};
2315+
auto inserted = LoweredCaptures.insert({fn, info});
23142316
assert(inserted.second && "already in map?!");
2315-
auto &cachedCaptures = inserted.first->second;
2316-
cachedCaptures.setGenericParamCaptures(capturesGenericParams);
2317-
cachedCaptures.setDynamicSelfType(capturesDynamicSelf);
2318-
cachedCaptures.setOpaqueValue(capturesOpaqueValue);
2319-
cachedCaptures.setCaptures(Context.AllocateCopy(resultingCaptures));
2320-
2321-
return cachedCaptures;
2317+
(void)inserted;
2318+
return info;
23222319
}
23232320

23242321
/// Given that type1 is known to be a subtype of type2, check if the two

lib/Sema/TypeCheckCaptures.cpp

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -42,39 +42,33 @@ class FindCapturedVars : public ASTWalker {
4242
OpaqueValueExpr *OpaqueValue = nullptr;
4343
SourceLoc CaptureLoc;
4444
DeclContext *CurDC;
45-
bool NoEscape, ObjC;
45+
bool NoEscape, ObjC, IsGenericFunction;
4646

4747
public:
4848
FindCapturedVars(ASTContext &Context,
4949
SourceLoc CaptureLoc,
5050
DeclContext *CurDC,
5151
bool NoEscape,
52-
bool ObjC)
52+
bool ObjC,
53+
bool IsGenericFunction)
5354
: Context(Context), CaptureLoc(CaptureLoc), CurDC(CurDC),
54-
NoEscape(NoEscape), ObjC(ObjC) {}
55+
NoEscape(NoEscape), ObjC(ObjC), IsGenericFunction(IsGenericFunction) {}
5556

5657
CaptureInfo getCaptureInfo() const {
57-
CaptureInfo result;
58-
59-
// Anything can capture an opaque value placeholder.
60-
if (OpaqueValue)
61-
result.setOpaqueValue(OpaqueValue);
58+
DynamicSelfType *dynamicSelfToRecord = nullptr;
59+
bool hasGenericParamCaptures = IsGenericFunction;
6260

6361
// Only local functions capture dynamic 'Self'.
6462
if (CurDC->getParent()->isLocalContext()) {
6563
if (GenericParamCaptureLoc.isValid())
66-
result.setGenericParamCaptures(true);
64+
hasGenericParamCaptures = true;
6765

6866
if (DynamicSelfCaptureLoc.isValid())
69-
result.setDynamicSelfType(DynamicSelf);
67+
dynamicSelfToRecord = DynamicSelf;
7068
}
7169

72-
if (Captures.empty())
73-
result.setCaptures(None);
74-
else
75-
result.setCaptures(Context.AllocateCopy(Captures));
76-
77-
return result;
70+
return CaptureInfo(Context, Captures, dynamicSelfToRecord, OpaqueValue,
71+
hasGenericParamCaptures);
7872
}
7973

8074
SourceLoc getGenericParamCaptureLoc() const {
@@ -590,28 +584,26 @@ void TypeChecker::computeCaptures(AnyFunctionRef AFR) {
590584

591585
PrettyStackTraceAnyFunctionRef trace("computing captures for", AFR);
592586

587+
// A generic function always captures outer generic parameters.
588+
bool isGeneric = false;
589+
auto *AFD = AFR.getAbstractFunctionDecl();
590+
if (AFD)
591+
isGeneric = (AFD->getGenericParams() != nullptr);
592+
593593
auto &Context = AFR.getAsDeclContext()->getASTContext();
594594
FindCapturedVars finder(Context,
595595
AFR.getLoc(),
596596
AFR.getAsDeclContext(),
597597
AFR.isKnownNoEscape(),
598-
AFR.isObjC());
598+
AFR.isObjC(),
599+
isGeneric);
599600
AFR.getBody()->walk(finder);
600601

601602
if (AFR.hasType() && !AFR.isObjC()) {
602603
finder.checkType(AFR.getType(), AFR.getLoc());
603604
}
604605

605-
// A generic function always captures outer generic parameters.
606-
bool isGeneric = false;
607-
auto *AFD = AFR.getAbstractFunctionDecl();
608-
if (AFD)
609-
isGeneric = AFD->isGeneric();
610-
611-
auto captures = finder.getCaptureInfo();
612-
if (isGeneric)
613-
captures.setGenericParamCaptures(true);
614-
AFR.setCaptureInfo(captures);
606+
AFR.setCaptureInfo(finder.getCaptureInfo());
615607

616608
// Compute captures for default argument expressions.
617609
if (auto *AFD = AFR.getAbstractFunctionDecl()) {
@@ -621,7 +613,8 @@ void TypeChecker::computeCaptures(AnyFunctionRef AFR) {
621613
E->getLoc(),
622614
AFD,
623615
/*isNoEscape=*/false,
624-
/*isObjC=*/false);
616+
/*isObjC=*/false,
617+
/*IsGeneric*/isGeneric);
625618
E->walk(finder);
626619

627620
if (!AFD->getDeclContext()->isLocalContext() &&
@@ -630,10 +623,7 @@ void TypeChecker::computeCaptures(AnyFunctionRef AFR) {
630623
diag::dynamic_self_default_arg);
631624
}
632625

633-
auto captures = finder.getCaptureInfo();
634-
if (isGeneric)
635-
captures.setGenericParamCaptures(true);
636-
P->setDefaultArgumentCaptureInfo(captures);
626+
P->setDefaultArgumentCaptureInfo(finder.getCaptureInfo());
637627
}
638628
}
639629
}
@@ -690,7 +680,8 @@ void TypeChecker::checkPatternBindingCaptures(NominalTypeDecl *typeDecl) {
690680
init->getLoc(),
691681
PBD->getInitContext(i),
692682
/*NoEscape=*/false,
693-
/*ObjC=*/false);
683+
/*ObjC=*/false,
684+
/*IsGenericFunction*/false);
694685
init->walk(finder);
695686

696687
if (finder.getDynamicSelfCaptureLoc().isValid() && !isLazy(PBD)) {

0 commit comments

Comments
 (0)