Skip to content

Commit 66d75f8

Browse files
committed
AST/SILGen: Requestify function body skipping.
Function bodies are skipped during typechecking when one of the -experimental-skip-*-function-bodies flags is passed to the frontend. This was implemented by setting the "body kind" of an `AbstractFunctionDecl` during decl checking in `TypeCheckDeclPrimary`. This approach had a couple of issues: - It is incompatible with skipping function bodies during lazy typechecking, since the skipping is only evaluated during a phase of eager typechecking. - It prevents skipped function bodies from being parsed on-demand ("skipped" is a state that is distinct from "parsed", when they ought to be orthogonal). This needlessly prevented complete module interfaces from being emitted with -experimental-skip-all-function-bodies. Storing the skipped status of a function separately from body kind and requestifying the determination of whether to skip a function solves these problems. Resolves rdar://116020403
1 parent 43e36cc commit 66d75f8

File tree

15 files changed

+182
-126
lines changed

15 files changed

+182
-126
lines changed

include/swift/AST/Decl.h

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -450,10 +450,13 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
450450
SWIFT_INLINE_BITFIELD(SubscriptDecl, VarDecl, 2,
451451
StaticSpelling : 2
452452
);
453-
SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+2+8+1+1+1+1+1+1+1,
453+
SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+2+2+8+1+1+1+1+1+1+1,
454454
/// \see AbstractFunctionDecl::BodyKind
455455
BodyKind : 3,
456456

457+
/// \see AbstractFunctionDecl::BodySkippedStatus
458+
BodySkippedStatus : 2,
459+
457460
/// \see AbstractFunctionDecl::SILSynthesizeKind
458461
SILSynthesizeKind : 2,
459462

@@ -6856,9 +6859,6 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
68566859
/// Function body is parsed and available as an AST subtree.
68576860
Parsed,
68586861

6859-
/// Function body is not available, although it was written in the source.
6860-
Skipped,
6861-
68626862
/// Function body will be synthesized on demand.
68636863
Synthesize,
68646864

@@ -6874,6 +6874,14 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
68746874
// This enum currently needs to fit in a 3-bit bitfield.
68756875
};
68766876

6877+
enum class BodySkippedStatus {
6878+
Unknown,
6879+
Skipped,
6880+
NotSkipped,
6881+
6882+
// This enum needs to fit in a 2-bit bitfield.
6883+
};
6884+
68776885
BodyKind getBodyKind() const {
68786886
return BodyKind(Bits.AbstractFunctionDecl.BodyKind);
68796887
}
@@ -6925,13 +6933,13 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
69256933

69266934
/// The location of the function body when the body is delayed or skipped.
69276935
///
6928-
/// This enum member is active if getBodyKind() is BodyKind::Unparsed or
6929-
/// BodyKind::Skipped.
6936+
/// This enum member is active if getBodyKind() is BodyKind::Unparsed.
69306937
SourceRange BodyRange;
69316938
};
69326939

69336940
friend class ParseAbstractFunctionBodyRequest;
69346941
friend class TypeCheckFunctionBodyRequest;
6942+
friend class IsFunctionBodySkippedRequest;
69356943

69366944
CaptureInfo Captures;
69376945

@@ -6969,6 +6977,15 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
69696977
Bits.AbstractFunctionDecl.BodyKind = unsigned(K);
69706978
}
69716979

6980+
BodySkippedStatus getBodySkippedStatus() const {
6981+
return static_cast<BodySkippedStatus>(
6982+
Bits.AbstractFunctionDecl.BodySkippedStatus);
6983+
}
6984+
6985+
void setBodySkippedStatus(BodySkippedStatus status) {
6986+
Bits.AbstractFunctionDecl.BodySkippedStatus = unsigned(status);
6987+
}
6988+
69726989
void setSILSynthesizeKind(SILSynthesizeKind K) {
69736990
Bits.AbstractFunctionDecl.SILSynthesizeKind = unsigned(K);
69746991
}
@@ -7087,10 +7104,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
70877104
///
70887105
/// Note that a true return value does not imply that the body was actually
70897106
/// parsed.
7090-
bool hasBody() const {
7091-
return getBodyKind() != BodyKind::None &&
7092-
getBodyKind() != BodyKind::Skipped;
7093-
}
7107+
bool hasBody() const { return getBodyKind() != BodyKind::None; }
70947108

70957109
/// Returns true if the text of this function's body can be retrieved either
70967110
/// by extracting the text from the source buffer or reading the inlinable
@@ -7112,21 +7126,6 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
71127126
/// Set a new body for the function.
71137127
void setBody(BraceStmt *S, BodyKind NewBodyKind);
71147128

7115-
/// Note that the body was skipped for this function. Function body
7116-
/// cannot be attached after this call.
7117-
void setBodySkipped(SourceRange bodyRange) {
7118-
// FIXME: Remove 'Parsed' from this list once we can always delay
7119-
// parsing bodies. The -experimental-skip-*-function-bodies options
7120-
// do currently skip parsing, unless disabled through other means in
7121-
// SourceFile::hasDelayedBodyParsing.
7122-
assert(getBodyKind() == BodyKind::None ||
7123-
getBodyKind() == BodyKind::Unparsed ||
7124-
getBodyKind() == BodyKind::Parsed);
7125-
assert(bodyRange.isValid());
7126-
BodyRange = bodyRange;
7127-
setBodyKind(BodyKind::Skipped);
7128-
}
7129-
71307129
/// Note that parsing for the body was delayed.
71317130
void setBodyDelayed(SourceRange bodyRange) {
71327131
assert(getBodyKind() == BodyKind::None);
@@ -7204,9 +7203,9 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
72047203
return getBodyKind() == BodyKind::SILSynthesize;
72057204
}
72067205

7207-
bool isBodySkipped() const {
7208-
return getBodyKind() == BodyKind::Skipped;
7209-
}
7206+
/// Indicates whether the body of this function is skipped during
7207+
/// typechecking.
7208+
bool isBodySkipped() const;
72107209

72117210
bool isMemberwiseInitializer() const {
72127211
return getBodyKind() == BodyKind::SILSynthesize

include/swift/AST/TypeCheckRequests.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4423,6 +4423,25 @@ class SerializeAttrGenericSignatureRequest
44234423
void cacheResult(GenericSignature signature) const;
44244424
};
44254425

4426+
class IsFunctionBodySkippedRequest
4427+
: public SimpleRequest<IsFunctionBodySkippedRequest,
4428+
bool(const AbstractFunctionDecl *),
4429+
RequestFlags::SeparatelyCached> {
4430+
public:
4431+
using SimpleRequest::SimpleRequest;
4432+
4433+
private:
4434+
friend SimpleRequest;
4435+
4436+
bool evaluate(Evaluator &evaluator, const AbstractFunctionDecl *) const;
4437+
4438+
public:
4439+
// Separate caching.
4440+
bool isCached() const { return true; }
4441+
llvm::Optional<bool> getCachedResult() const;
4442+
void cacheResult(bool isSkipped) const;
4443+
};
4444+
44264445
#define SWIFT_TYPEID_ZONE TypeChecker
44274446
#define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def"
44284447
#include "swift/Basic/DefineTypeIDZone.h"

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,3 +503,6 @@ SWIFT_REQUEST(TypeChecker, ExpandChildTypeRefinementContextsRequest,
503503
SWIFT_REQUEST(TypeChecker, SerializeAttrGenericSignatureRequest,
504504
bool(Decl *, SpecializeAttr *),
505505
Cached, NoLocationInfo)
506+
SWIFT_REQUEST(TypeChecker, IsFunctionBodySkippedRequest,
507+
bool(const AbstractFunctionDecl *),
508+
SeparatelyCached, NoLocationInfo)

lib/AST/ASTVerifier.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,6 @@ class Verifier : public ASTWalker {
496496
switch (afd->getBodyKind()) {
497497
case AbstractFunctionDecl::BodyKind::None:
498498
case AbstractFunctionDecl::BodyKind::TypeChecked:
499-
case AbstractFunctionDecl::BodyKind::Skipped:
500499
case AbstractFunctionDecl::BodyKind::SILSynthesize:
501500
case AbstractFunctionDecl::BodyKind::Deserialized:
502501
return true;

lib/AST/Decl.cpp

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8934,9 +8934,6 @@ BraceStmt *AbstractFunctionDecl::getTypecheckedBody() const {
89348934
}
89358935

89368936
void AbstractFunctionDecl::setBody(BraceStmt *S, BodyKind NewBodyKind) {
8937-
assert(getBodyKind() != BodyKind::Skipped &&
8938-
"cannot set a body if it was skipped");
8939-
89408937
llvm::Optional<Fingerprint> fp = llvm::None;
89418938
if (getBodyKind() == BodyKind::TypeChecked ||
89428939
getBodyKind() == BodyKind::Parsed) {
@@ -8952,6 +8949,52 @@ void AbstractFunctionDecl::setBody(BraceStmt *S, BodyKind NewBodyKind) {
89528949
}
89538950
}
89548951

8952+
bool AbstractFunctionDecl::isBodySkipped() const {
8953+
return evaluateOrDefault(getASTContext().evaluator, IsFunctionBodySkippedRequest{this}, false);
8954+
}
8955+
8956+
bool IsFunctionBodySkippedRequest::evaluate(
8957+
Evaluator &evaluator, const AbstractFunctionDecl *afd) const {
8958+
auto &Ctx = afd->getASTContext();
8959+
// Make sure we're in a mode that's skipping function bodies.
8960+
if (Ctx.TypeCheckerOpts.SkipFunctionBodies == FunctionBodySkipping::None)
8961+
return false;
8962+
8963+
if (auto *AD = dyn_cast<AccessorDecl>(afd)) {
8964+
// didSet needs to be checked to determine whether to keep its parameter,
8965+
// so never try to skip.
8966+
if (AD->getAccessorKind() == AccessorKind::DidSet)
8967+
return false;
8968+
8969+
// Synthesized accessors (like implicit _read or _modify accessors) need to
8970+
// be typechecked because they get serialized.
8971+
if (AD->isSynthesized())
8972+
return false;
8973+
}
8974+
8975+
// Do not skip the body of an actor initializer. They are checked to determine
8976+
// delegation status.
8977+
if (auto *ctor = dyn_cast<ConstructorDecl>(afd))
8978+
if (auto *nom = ctor->getParent()->getSelfNominalTypeDecl())
8979+
if (nom->isAnyActor())
8980+
return false;
8981+
8982+
// Skipping all bodies won't serialize anything, so we can skip regardless.
8983+
if (Ctx.TypeCheckerOpts.SkipFunctionBodies == FunctionBodySkipping::All)
8984+
return true;
8985+
8986+
// If we want all types (for LLDB) we can't skip functions with nested
8987+
// types. We could probably improve upon this and type-check only the
8988+
// nested types instead for better performances.
8989+
if (afd->hasNestedTypeDeclarations() &&
8990+
Ctx.TypeCheckerOpts.SkipFunctionBodies ==
8991+
FunctionBodySkipping::NonInlinableWithoutTypes)
8992+
return false;
8993+
8994+
// Only skip functions where their body won't be serialized
8995+
return afd->getResilienceExpansion() != ResilienceExpansion::Minimal;
8996+
}
8997+
89558998
void AbstractFunctionDecl::setBodyToBeReparsed(SourceRange bodyRange) {
89568999
assert(bodyRange.isValid());
89579000
assert(getBodyKind() == BodyKind::Unparsed ||
@@ -8986,7 +9029,6 @@ SourceRange AbstractFunctionDecl::getBodySourceRange() const {
89869029

89879030
return SourceRange();
89889031

8989-
case BodyKind::Skipped:
89909032
case BodyKind::Unparsed:
89919033
return BodyRange;
89929034
}
@@ -9396,7 +9438,6 @@ bool AbstractFunctionDecl::hasInlinableBodyText() const {
93969438

93979439
case BodyKind::None:
93989440
case BodyKind::Synthesize:
9399-
case BodyKind::Skipped:
94009441
case BodyKind::SILSynthesize:
94019442
return false;
94029443
}
@@ -9847,8 +9888,7 @@ SourceRange FuncDecl::getSourceRange() const {
98479888
if (StartLoc.isInvalid())
98489889
return SourceRange();
98499890

9850-
if (getBodyKind() == BodyKind::Unparsed ||
9851-
getBodyKind() == BodyKind::Skipped)
9891+
if (getBodyKind() == BodyKind::Unparsed)
98529892
return { StartLoc, BodyRange.End };
98539893

98549894
SourceLoc RBraceLoc = getOriginalBodySourceRange().End;
@@ -10530,7 +10570,6 @@ ParseAbstractFunctionBodyRequest::getCachedResult() const {
1053010570
case BodyKind::Deserialized:
1053110571
case BodyKind::SILSynthesize:
1053210572
case BodyKind::None:
10533-
case BodyKind::Skipped:
1053410573
return BodyAndFingerprint{};
1053510574

1053610575
case BodyKind::TypeChecked:
@@ -10552,7 +10591,6 @@ void ParseAbstractFunctionBodyRequest::cacheResult(
1055210591
case BodyKind::Deserialized:
1055310592
case BodyKind::SILSynthesize:
1055410593
case BodyKind::None:
10555-
case BodyKind::Skipped:
1055610594
// The body is always empty, so don't cache anything.
1055710595
assert(!value.getFingerprint().has_value() && value.getBody() == nullptr);
1055810596
return;
@@ -10569,6 +10607,27 @@ void ParseAbstractFunctionBodyRequest::cacheResult(
1056910607
}
1057010608
}
1057110609

10610+
llvm::Optional<bool> IsFunctionBodySkippedRequest::getCachedResult() const {
10611+
using BodySkippedStatus = AbstractFunctionDecl::BodySkippedStatus;
10612+
auto afd = std::get<0>(getStorage());
10613+
switch (afd->getBodySkippedStatus()) {
10614+
case BodySkippedStatus::Unknown:
10615+
return llvm::None;
10616+
case BodySkippedStatus::Skipped:
10617+
return true;
10618+
case BodySkippedStatus::NotSkipped:
10619+
return false;
10620+
}
10621+
llvm_unreachable("bad BodySkippedStatus");
10622+
}
10623+
10624+
void IsFunctionBodySkippedRequest::cacheResult(bool isSkipped) const {
10625+
using BodySkippedStatus = AbstractFunctionDecl::BodySkippedStatus;
10626+
auto afd = std::get<0>(getStorage());
10627+
const_cast<AbstractFunctionDecl *>(afd)->setBodySkippedStatus(
10628+
isSkipped ? BodySkippedStatus::Skipped : BodySkippedStatus::NotSkipped);
10629+
}
10630+
1057210631
void swift::simple_display(llvm::raw_ostream &out, BodyAndFingerprint value) {
1057310632
out << "(";
1057410633
simple_display(out, value.getBody());

lib/AST/TypeCheckRequests.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1457,11 +1457,13 @@ llvm::Optional<BraceStmt *>
14571457
TypeCheckFunctionBodyRequest::getCachedResult() const {
14581458
using BodyKind = AbstractFunctionDecl::BodyKind;
14591459
auto *afd = std::get<0>(getStorage());
1460+
if (afd->isBodySkipped())
1461+
return nullptr;
1462+
14601463
switch (afd->getBodyKind()) {
14611464
case BodyKind::Deserialized:
14621465
case BodyKind::SILSynthesize:
14631466
case BodyKind::None:
1464-
case BodyKind::Skipped:
14651467
// These cases don't have any body available.
14661468
return nullptr;
14671469

lib/Parse/ParseRequests.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ ParseAbstractFunctionBodyRequest::evaluate(Evaluator &evaluator,
9191
case BodyKind::Deserialized:
9292
case BodyKind::SILSynthesize:
9393
case BodyKind::None:
94-
case BodyKind::Skipped:
9594
return {};
9695

9796
case BodyKind::TypeChecked:

lib/SIL/IR/Linker.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
#include "swift/AST/SubstitutionMap.h"
6262
#include "swift/ClangImporter/ClangModule.h"
6363
#include "swift/SIL/FormalLinkage.h"
64+
#include "swift/SIL/PrettyStackTrace.h"
6465
#include "swift/Serialization/SerializedSILLoader.h"
6566
#include <functional>
6667

@@ -100,6 +101,8 @@ void SILLinkerVisitor::deserializeAndPushToWorklist(SILFunction *F) {
100101
/// Deserialize a function and add it to the worklist for processing.
101102
void SILLinkerVisitor::maybeAddFunctionToWorklist(SILFunction *F,
102103
bool setToSerializable) {
104+
PrettyStackTraceSILFunction stackTrace("adding to linker worklist", F);
105+
103106
SILLinkage linkage = F->getLinkage();
104107
assert((!setToSerializable || F->hasValidLinkageForFragileRef() ||
105108
hasSharedVisibility(linkage)) &&

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6676,13 +6676,20 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
66766676
SILModule &mod = F->getModule();
66776677
bool embedded = mod.getASTContext().LangOpts.hasFeature(Feature::Embedded);
66786678

6679+
bool isBodySkipped = false;
6680+
if (auto DC = F->getDeclContext()) {
6681+
if (auto AFD = dyn_cast<AbstractFunctionDecl>(DC)) {
6682+
isBodySkipped = AFD->isBodySkipped();
6683+
}
6684+
}
6685+
66796686
require(!F->isSerialized() || !mod.isSerialized() || mod.isParsedAsSerializedSIL(),
66806687
"cannot have a serialized function after the module has been serialized");
66816688

66826689
switch (F->getLinkage()) {
66836690
case SILLinkage::Public:
66846691
case SILLinkage::Shared:
6685-
require(F->isDefinition() || F->hasForeignBody(),
6692+
require(isBodySkipped || F->isDefinition() || F->hasForeignBody(),
66866693
"public/shared function must have a body");
66876694
break;
66886695
case SILLinkage::PublicNonABI:
@@ -6711,13 +6718,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
67116718

67126719
// Don't verify functions that were skipped. We are likely to see them in
67136720
// FunctionBodySkipping::NonInlinableWithoutTypes mode.
6714-
auto Ctx = F->getDeclContext();
6715-
if (Ctx) {
6716-
if (auto AFD = dyn_cast<AbstractFunctionDecl>(Ctx)) {
6717-
if (AFD->isBodySkipped())
6718-
return;
6719-
}
6720-
}
6721+
if (isBodySkipped)
6722+
return;
67216723

67226724
if (F->isExternalDeclaration()) {
67236725
if (F->hasForeignBody())

0 commit comments

Comments
 (0)