Skip to content

Commit abe8b37

Browse files
committed
[Type refinement context] Lazily expand TRCs for pattern bindings
Eager expansion of type refinement contexts (TRCs) for variables within pattern binding declarations is causing cyclic references in some places involving macros. Make this expansion lazy, triggered by walking into these pattern binding declarations as part of (e.g.) availability queries. Another step toward fixing the cyclic references in rdar://112079160.
1 parent 71f5405 commit abe8b37

File tree

5 files changed

+152
-56
lines changed

5 files changed

+152
-56
lines changed

include/swift/AST/TypeCheckRequests.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class TypeAliasDecl;
6363
class TypeLoc;
6464
class Witness;
6565
class TypeResolution;
66+
class TypeRefinementContext;
6667
struct TypeWitnessAndDecl;
6768
class ValueDecl;
6869
enum class OpaqueReadOwnership: uint8_t;
@@ -4387,6 +4388,25 @@ class InitAccessorReferencedVariablesRequest
43874388
bool isCached() const { return true; }
43884389
};
43894390

4391+
/// Expand the children of the type refinement context for the given
4392+
/// declaration.
4393+
class ExpandChildTypeRefinementContextsRequest
4394+
: public SimpleRequest<ExpandChildTypeRefinementContextsRequest,
4395+
bool(Decl *, TypeRefinementContext *),
4396+
RequestFlags::Cached> {
4397+
public:
4398+
using SimpleRequest::SimpleRequest;
4399+
4400+
private:
4401+
friend SimpleRequest;
4402+
4403+
bool evaluate(Evaluator &evaluator, Decl *decl,
4404+
TypeRefinementContext *parentTRC) const;
4405+
4406+
public:
4407+
bool isCached() const { return true; }
4408+
};
4409+
43904410
#define SWIFT_TYPEID_ZONE TypeChecker
43914411
#define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def"
43924412
#include "swift/Basic/DefineTypeIDZone.h"

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,3 +500,6 @@ SWIFT_REQUEST(TypeChecker, InitAccessorReferencedVariablesRequest,
500500
ArrayRef<VarDecl *>(DeclAttribute *, AccessorDecl *,
501501
ArrayRef<Identifier>),
502502
Cached, NoLocationInfo)
503+
SWIFT_REQUEST(TypeChecker, ExpandChildTypeRefinementContextsRequest,
504+
bool(Decl *, TypeRefinementContext *),
505+
Cached, NoLocationInfo)

include/swift/AST/TypeRefinementContext.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,9 @@ class TypeRefinementContext : public ASTAllocated<TypeRefinementContext> {
297297
static StringRef getReasonName(Reason R);
298298
};
299299

300+
void simple_display(llvm::raw_ostream &out,
301+
const TypeRefinementContext *trc);
302+
300303
} // end namespace swift
301304

302305
#endif

lib/AST/TypeRefinementContext.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/AST/Stmt.h"
2121
#include "swift/AST/Expr.h"
2222
#include "swift/AST/SourceFile.h"
23+
#include "swift/AST/TypeCheckRequests.h"
2324
#include "swift/AST/TypeRefinementContext.h"
2425
#include "swift/Basic/SourceManager.h"
2526

@@ -192,6 +193,18 @@ TypeRefinementContext::findMostRefinedSubContext(SourceLoc Loc,
192193
!rangeContainsTokenLocWithGeneratedSource(SM, SrcRange, Loc))
193194
return nullptr;
194195

196+
// If this context is for a declaration, ensure that we've expanded the
197+
// children of the declaration.
198+
if (Node.getReason() == Reason::Decl ||
199+
Node.getReason() == Reason::DeclImplicit) {
200+
if (auto decl = Node.getAsDecl()) {
201+
ASTContext &ctx = decl->getASTContext();
202+
(void)evaluateOrDefault(
203+
ctx.evaluator, ExpandChildTypeRefinementContextsRequest{decl, this},
204+
false);
205+
}
206+
}
207+
195208
// For the moment, we perform a linear search here, but we can and should
196209
// do something more efficient.
197210
for (TypeRefinementContext *Child : Children) {
@@ -411,3 +424,8 @@ StringRef TypeRefinementContext::getReasonName(Reason R) {
411424

412425
llvm_unreachable("Unhandled Reason in switch.");
413426
}
427+
428+
void swift::simple_display(
429+
llvm::raw_ostream &out, const TypeRefinementContext *trc) {
430+
out << "TRC @" << trc;
431+
}

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 108 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,8 @@ class TypeRefinementContextBuilder : private ASTWalker {
440440
return "building type refinement context for";
441441
}
442442

443+
friend class swift::ExpandChildTypeRefinementContextsRequest;
444+
443445
public:
444446
TypeRefinementContextBuilder(TypeRefinementContext *TRC, ASTContext &Context)
445447
: Context(Context) {
@@ -576,9 +578,10 @@ class TypeRefinementContextBuilder : private ASTWalker {
576578
// pattern. The context necessary for the pattern as a whole was already
577579
// introduced if necessary by the first var decl.
578580
if (auto *VD = dyn_cast<VarDecl>(D)) {
579-
if (auto *PBD = VD->getParentPatternBinding())
581+
if (auto *PBD = VD->getParentPatternBinding()) {
580582
if (VD != PBD->getAnchoringVarDecl(0))
581583
return nullptr;
584+
}
582585
}
583586

584587
// Declarations with an explicit availability attribute always get a TRC.
@@ -597,11 +600,16 @@ class TypeRefinementContextBuilder : private ASTWalker {
597600
// internal property in a public struct can be effectively less available
598601
// than the containing struct decl because the internal property will only
599602
// be accessed by code running at the deployment target or later.
603+
//
604+
// For declarations that have their child construction delayed, always
605+
// create this implicit declaration context. It will be used to trigger
606+
// lazy creation of the child TRCs.
600607
AvailabilityContext CurrentAvailability =
601608
getCurrentTRC()->getAvailabilityInfo();
602609
AvailabilityContext EffectiveAvailability =
603610
getEffectiveAvailabilityForDeclSignature(D, CurrentAvailability);
604-
if (CurrentAvailability.isSupersetOf(EffectiveAvailability))
611+
if (isa<VarDecl>(D) ||
612+
CurrentAvailability.isSupersetOf(EffectiveAvailability))
605613
return TypeRefinementContext::createForDeclImplicit(
606614
Context, D, getCurrentTRC(), EffectiveAvailability,
607615
refinementSourceRangeForDecl(D));
@@ -709,28 +717,79 @@ class TypeRefinementContextBuilder : private ASTWalker {
709717
return D->getSourceRange();
710718
}
711719

720+
// Creates an implicit decl TRC specifying the deployment
721+
// target for `range` in decl `D`.
722+
TypeRefinementContext *
723+
createImplicitDeclContextForDeploymentTarget(Decl *D, SourceRange range){
724+
AvailabilityContext Availability =
725+
AvailabilityContext::forDeploymentTarget(Context);
726+
Availability.intersectWith(getCurrentTRC()->getAvailabilityInfo());
727+
728+
return TypeRefinementContext::createForDeclImplicit(
729+
Context, D, getCurrentTRC(), Availability, range);
730+
}
731+
732+
/// Build contexts for a VarDecl with the given initializer.
733+
void buildContextsForPatternBindingDecl(PatternBindingDecl *pattern) {
734+
// Build contexts for each of the pattern entries.
735+
for (unsigned index : range(pattern->getNumPatternEntries())) {
736+
auto var = pattern->getAnchoringVarDecl(index);
737+
if (!var)
738+
continue;
739+
740+
// Var decls may have associated pattern binding decls or property wrappers
741+
// with init expressions. Those expressions need to be constrained to the
742+
// deployment target unless they are exposed to clients.
743+
if (!var->hasInitialValue() || var->isInitExposedToClients())
744+
continue;
745+
746+
auto *initExpr = pattern->getInit(index);
747+
if (initExpr && !initExpr->isImplicit()) {
748+
assert(initExpr->getSourceRange().isValid());
749+
750+
// Create a TRC for the init written in the source. The ASTWalker
751+
// won't visit these expressions so instead of pushing these onto the
752+
// stack we build them directly.
753+
auto *initTRC = createImplicitDeclContextForDeploymentTarget(
754+
var, initExpr->getSourceRange());
755+
TypeRefinementContextBuilder(initTRC, Context).build(initExpr);
756+
}
757+
}
758+
759+
// Ideally any init expression would be returned by `getInit()` above.
760+
// However, for property wrappers it doesn't get populated until
761+
// typechecking completes (which is too late). Instead, we find the
762+
// the property wrapper attribute and use its source range to create a
763+
// TRC for the initializer expression.
764+
//
765+
// FIXME: Since we don't have an expression here, we can't build out its
766+
// TRC. If the Expr that will eventually be created contains a closure
767+
// expression, then it might have AST nodes that need to be refined. For
768+
// example, property wrapper initializers that takes block arguments
769+
// are not handled correctly because of this (rdar://77841331).
770+
if (auto firstVar = pattern->getAnchoringVarDecl(0)) {
771+
if (firstVar->hasInitialValue() && !firstVar->isInitExposedToClients()) {
772+
for (auto *wrapper : firstVar->getAttachedPropertyWrappers()) {
773+
createImplicitDeclContextForDeploymentTarget(
774+
firstVar, wrapper->getRange());
775+
}
776+
}
777+
}
778+
}
779+
712780
void buildContextsForBodyOfDecl(Decl *D) {
713781
// Are we already constrained by the deployment target? If not, adding
714782
// new contexts won't change availability.
715783
if (isCurrentTRCContainedByDeploymentTarget())
716784
return;
717785

718-
// A lambda that creates an implicit decl TRC specifying the deployment
719-
// target for `range` in decl `D`.
720-
auto createContext = [this](Decl *D, SourceRange range) {
721-
AvailabilityContext Availability =
722-
AvailabilityContext::forDeploymentTarget(Context);
723-
Availability.intersectWith(getCurrentTRC()->getAvailabilityInfo());
724-
725-
return TypeRefinementContext::createForDeclImplicit(
726-
Context, D, getCurrentTRC(), Availability, range);
727-
};
728-
729786
// Top level code always uses the deployment target.
730787
if (auto tlcd = dyn_cast<TopLevelCodeDecl>(D)) {
731788
if (auto bodyStmt = tlcd->getBody()) {
732-
pushDeclBodyContext(createContext(tlcd, tlcd->getSourceRange()), tlcd,
733-
bodyStmt);
789+
pushDeclBodyContext(
790+
createImplicitDeclContextForDeploymentTarget(
791+
tlcd, tlcd->getSourceRange()),
792+
tlcd, bodyStmt);
734793
}
735794
return;
736795
}
@@ -741,51 +800,14 @@ class TypeRefinementContextBuilder : private ASTWalker {
741800
if (!afd->isImplicit() &&
742801
afd->getResilienceExpansion() != ResilienceExpansion::Minimal) {
743802
if (auto body = afd->getBody(/*canSynthesize*/ false)) {
744-
pushDeclBodyContext(createContext(afd, afd->getBodySourceRange()),
745-
afd, body);
803+
pushDeclBodyContext(
804+
createImplicitDeclContextForDeploymentTarget(
805+
afd, afd->getBodySourceRange()),
806+
afd, body);
746807
}
747808
}
748809
return;
749810
}
750-
751-
// Var decls may have associated pattern binding decls or property wrappers
752-
// with init expressions. Those expressions need to be constrained to the
753-
// deployment target unless they are exposed to clients.
754-
if (auto vd = dyn_cast<VarDecl>(D)) {
755-
if (!vd->hasInitialValue() || vd->isInitExposedToClients())
756-
return;
757-
758-
if (auto *pbd = vd->getParentPatternBinding()) {
759-
int idx = pbd->getPatternEntryIndexForVarDecl(vd);
760-
auto *initExpr = pbd->getInit(idx);
761-
if (initExpr && !initExpr->isImplicit()) {
762-
assert(initExpr->getSourceRange().isValid());
763-
764-
// Create a TRC for the init written in the source. The ASTWalker
765-
// won't visit these expressions so instead of pushing these onto the
766-
// stack we build them directly.
767-
auto *initTRC = createContext(vd, initExpr->getSourceRange());
768-
TypeRefinementContextBuilder(initTRC, Context).build(initExpr);
769-
}
770-
771-
// Ideally any init expression would be returned by `getInit()` above.
772-
// However, for property wrappers it doesn't get populated until
773-
// typechecking completes (which is too late). Instead, we find the
774-
// the property wrapper attribute and use its source range to create a
775-
// TRC for the initializer expression.
776-
//
777-
// FIXME: Since we don't have an expression here, we can't build out its
778-
// TRC. If the Expr that will eventually be created contains a closure
779-
// expression, then it might have AST nodes that need to be refined. For
780-
// example, property wrapper initializers that takes block arguments
781-
// are not handled correctly because of this (rdar://77841331).
782-
for (auto *wrapper : vd->getAttachedPropertyWrappers()) {
783-
createContext(vd, wrapper->getRange());
784-
}
785-
}
786-
787-
return;
788-
}
789811
}
790812

791813
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
@@ -1277,6 +1299,36 @@ TypeChecker::getOrBuildTypeRefinementContext(SourceFile *SF) {
12771299
return TRC;
12781300
}
12791301

1302+
bool ExpandChildTypeRefinementContextsRequest::evaluate(
1303+
Evaluator &evaluator, Decl *decl, TypeRefinementContext *parentTRC
1304+
) const {
1305+
// If the parent TRC is already contained by the deployment target, there's
1306+
// nothing more to do.
1307+
ASTContext &ctx = decl->getASTContext();
1308+
if (computeContainedByDeploymentTarget(parentTRC, ctx))
1309+
return false;
1310+
1311+
// Variables can have children corresponding to property wrappers and
1312+
// the initial values provided in each pattern binding entry.
1313+
if (auto var = dyn_cast<VarDecl>(decl)) {
1314+
if (auto *pattern = var->getParentPatternBinding()) {
1315+
// Only do this for the first variable in the pattern binding declaration.
1316+
auto anchorVar = pattern->getAnchoringVarDecl(0);
1317+
if (anchorVar != var) {
1318+
return evaluateOrDefault(
1319+
evaluator,
1320+
ExpandChildTypeRefinementContextsRequest{anchorVar, parentTRC},
1321+
false);
1322+
}
1323+
1324+
TypeRefinementContextBuilder builder(parentTRC, ctx);
1325+
builder.buildContextsForPatternBindingDecl(pattern);
1326+
}
1327+
}
1328+
1329+
return false;
1330+
}
1331+
12801332
AvailabilityContext
12811333
TypeChecker::overApproximateAvailabilityAtLocation(SourceLoc loc,
12821334
const DeclContext *DC,

0 commit comments

Comments
 (0)