Skip to content

Commit c8e63ff

Browse files
committed
Sema: Refactor TypeRefinementContextBuilder pattern init handling.
Previously, `TypeRefinementContextBuilder` would walk into the initializer expressions of pattern binding entries twice, resulting in duplicate descendants of the TRC nodes representing pattern binding declarations.
1 parent b33a71d commit c8e63ff

File tree

2 files changed

+120
-60
lines changed

2 files changed

+120
-60
lines changed

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 67 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -418,10 +418,14 @@ class TypeRefinementContextBuilder : private ASTWalker {
418418
ContextStack.push_back(Info);
419419
}
420420

421-
void pushDeclBodyContext(TypeRefinementContext *TRC, Decl *D, ASTNode Body) {
421+
void pushDeclBodyContext(
422+
Decl *D, llvm::SmallVector<std::pair<ASTNode, TypeRefinementContext *>, 4>
423+
NodesAndTRCs) {
422424
DeclBodyContextInfo Info;
423425
Info.Decl = D;
424-
Info.BodyTRCs.insert({Body, TRC});
426+
for (auto NodeAndTRC : NodesAndTRCs) {
427+
Info.BodyTRCs.insert(NodeAndTRC);
428+
}
425429

426430
DeclBodyContextStack.push_back(Info);
427431
}
@@ -692,54 +696,6 @@ class TypeRefinementContextBuilder : private ASTWalker {
692696
Context, D, getCurrentTRC(), Availability, range);
693697
}
694698

695-
/// Build contexts for a pattern binding declaration.
696-
void buildContextsForPatternBindingDecl(PatternBindingDecl *pattern) {
697-
// Build contexts for each of the pattern entries.
698-
for (unsigned index : range(pattern->getNumPatternEntries())) {
699-
auto var = pattern->getAnchoringVarDecl(index);
700-
if (!var)
701-
continue;
702-
703-
// Var decls may have associated pattern binding decls or property wrappers
704-
// with init expressions. Those expressions need to be constrained to the
705-
// deployment target unless they are exposed to clients.
706-
if (!var->hasInitialValue() || var->isInitExposedToClients())
707-
continue;
708-
709-
auto *initExpr = pattern->getInit(index);
710-
if (initExpr && !initExpr->isImplicit()) {
711-
assert(initExpr->getSourceRange().isValid());
712-
713-
// Create a TRC for the init written in the source. The ASTWalker
714-
// won't visit these expressions so instead of pushing these onto the
715-
// stack we build them directly.
716-
auto *initTRC = createImplicitDeclContextForDeploymentTarget(
717-
var, initExpr->getSourceRange());
718-
TypeRefinementContextBuilder(initTRC, Context).build(initExpr);
719-
}
720-
}
721-
722-
// Ideally any init expression would be returned by `getInit()` above.
723-
// However, for property wrappers it doesn't get populated until
724-
// typechecking completes (which is too late). Instead, we find the
725-
// the property wrapper attribute and use its source range to create a
726-
// TRC for the initializer expression.
727-
//
728-
// FIXME: Since we don't have an expression here, we can't build out its
729-
// TRC. If the Expr that will eventually be created contains a closure
730-
// expression, then it might have AST nodes that need to be refined. For
731-
// example, property wrapper initializers that takes block arguments
732-
// are not handled correctly because of this (rdar://77841331).
733-
if (auto firstVar = pattern->getAnchoringVarDecl(0)) {
734-
if (firstVar->hasInitialValue() && !firstVar->isInitExposedToClients()) {
735-
for (auto *wrapper : firstVar->getAttachedPropertyWrappers()) {
736-
createImplicitDeclContextForDeploymentTarget(
737-
firstVar, wrapper->getRange());
738-
}
739-
}
740-
}
741-
}
742-
743699
void buildContextsForBodyOfDecl(Decl *D) {
744700
// Are we already constrained by the deployment target? If not, adding
745701
// new contexts won't change availability.
@@ -750,9 +706,8 @@ class TypeRefinementContextBuilder : private ASTWalker {
750706
if (auto tlcd = dyn_cast<TopLevelCodeDecl>(D)) {
751707
if (auto bodyStmt = tlcd->getBody()) {
752708
pushDeclBodyContext(
753-
createImplicitDeclContextForDeploymentTarget(
754-
tlcd, tlcd->getSourceRange()),
755-
tlcd, bodyStmt);
709+
tlcd, {{bodyStmt, createImplicitDeclContextForDeploymentTarget(
710+
tlcd, tlcd->getSourceRange())}});
756711
}
757712
return;
758713
}
@@ -764,16 +719,65 @@ class TypeRefinementContextBuilder : private ASTWalker {
764719
afd->getResilienceExpansion() != ResilienceExpansion::Minimal) {
765720
if (auto body = afd->getBody(/*canSynthesize*/ false)) {
766721
pushDeclBodyContext(
767-
createImplicitDeclContextForDeploymentTarget(
768-
afd, afd->getBodySourceRange()),
769-
afd, body);
722+
afd, {{body, createImplicitDeclContextForDeploymentTarget(
723+
afd, afd->getBodySourceRange())}});
770724
}
771725
}
772726
return;
773727
}
774728

775-
if (auto pattern = dyn_cast<PatternBindingDecl>(D)) {
776-
buildContextsForPatternBindingDecl(pattern);
729+
// Pattern binding declarations can have children corresponding to property
730+
// wrappers and the initial values provided in each pattern binding entry
731+
if (auto *pbd = dyn_cast<PatternBindingDecl>(D)) {
732+
llvm::SmallVector<std::pair<ASTNode, TypeRefinementContext *>, 4>
733+
nodesAndTRCs;
734+
735+
for (unsigned index : range(pbd->getNumPatternEntries())) {
736+
auto var = pbd->getAnchoringVarDecl(index);
737+
if (!var)
738+
continue;
739+
740+
// Var decls may have associated pattern binding decls or property
741+
// wrappers with init expressions. Those expressions need to be
742+
// constrained to the deployment target unless they are exposed to
743+
// clients.
744+
if (!var->hasInitialValue() || var->isInitExposedToClients())
745+
continue;
746+
747+
auto *initExpr = pbd->getInit(index);
748+
if (initExpr && !initExpr->isImplicit()) {
749+
assert(initExpr->getSourceRange().isValid());
750+
751+
// Create a TRC for the init written in the source.
752+
nodesAndTRCs.push_back(
753+
{initExpr, createImplicitDeclContextForDeploymentTarget(
754+
var, initExpr->getSourceRange())});
755+
}
756+
}
757+
758+
if (nodesAndTRCs.size() > 0)
759+
pushDeclBodyContext(pbd, nodesAndTRCs);
760+
761+
// Ideally any init expression would be returned by `getInit()` above.
762+
// However, for property wrappers it doesn't get populated until
763+
// typechecking completes (which is too late). Instead, we find the
764+
// the property wrapper attribute and use its source range to create a
765+
// TRC for the initializer expression.
766+
//
767+
// FIXME: Since we don't have an expression here, we can't build out its
768+
// TRC. If the Expr that will eventually be created contains a closure
769+
// expression, then it might have AST nodes that need to be refined. For
770+
// example, property wrapper initializers that takes block arguments
771+
// are not handled correctly because of this (rdar://77841331).
772+
if (auto firstVar = pbd->getAnchoringVarDecl(0)) {
773+
if (firstVar->hasInitialValue() &&
774+
!firstVar->isInitExposedToClients()) {
775+
for (auto *wrapper : firstVar->getAttachedPropertyWrappers()) {
776+
createImplicitDeclContextForDeploymentTarget(firstVar,
777+
wrapper->getRange());
778+
}
779+
}
780+
}
777781
return;
778782
}
779783
}
@@ -1200,6 +1204,11 @@ class TypeRefinementContextBuilder : private ASTWalker {
12001204
return AvailabilityContext(VersionRange::allGTE(Version));
12011205
}
12021206

1207+
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
1208+
(void)consumeDeclBodyContextIfNecessary(E);
1209+
return Action::Continue(E);
1210+
}
1211+
12031212
PostWalkResult<Expr *> walkToExprPost(Expr *E) override {
12041213
if (ContextStack.back().ScopeNode.getAsExpr() == E) {
12051214
ContextStack.pop_back();

test/attr/attr_inlinable_available.swift

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,36 +42,43 @@
4242
/// inlining target.
4343
public struct NoAvailable {
4444
@usableFromInline internal init() {}
45+
init<T>(_ t: T) {}
4546
}
4647

4748
@available(macOS 10.9, *)
4849
public struct BeforeInliningTarget {
4950
@usableFromInline internal init() {}
51+
init<T>(_ t: T) {}
5052
}
5153

5254
@available(macOS 10.10, *)
5355
public struct AtInliningTarget {
5456
@usableFromInline internal init() {}
57+
init<T>(_ t: T) {}
5558
}
5659

5760
@available(macOS 10.14.5, *)
5861
public struct BetweenTargets { // expected-note {{enclosing scope requires availability of macOS 10.14.5 or newer}}
5962
@usableFromInline internal init() {}
63+
init<T>(_ t: T) {}
6064
}
6165

6266
@available(macOS 10.15, *)
6367
public struct AtDeploymentTarget {
6468
@usableFromInline internal init() {}
69+
init<T>(_ t: T) {}
6570
}
6671

6772
@available(macOS 11, *)
6873
public struct AfterDeploymentTarget {
6974
@usableFromInline internal init() {}
75+
init<T>(_ t: T) {}
7076
}
7177

7278
@available(macOS, unavailable)
7379
public struct Unavailable {
7480
@usableFromInline internal init() {}
81+
init<T>(_ t: T) {}
7582
}
7683

7784
// MARK: - Protocol definitions
@@ -880,7 +887,7 @@ public struct PropertyWrapper<T> {
880887
public init(_ value: T) { self.wrappedValue = value }
881888
}
882889

883-
public struct PublicStruct { // expected-note 15 {{add @available attribute}}
890+
public struct PublicStruct { // expected-note 21 {{add @available attribute}}
884891
// Public property declarations are exposed.
885892
public var aPublic: NoAvailable,
886893
bPublic: BeforeInliningTarget,
@@ -958,7 +965,51 @@ public struct PublicStruct { // expected-note 15 {{add @available attribute}}
958965
dPublicInit: Any = BetweenTargets(),
959966
ePublicInit: Any = AtDeploymentTarget(),
960967
fPublicInit: Any = AfterDeploymentTarget() // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}}
961-
968+
969+
@available(macOS 10.14.5, *)
970+
public var aPublicInitAvailBetween: Any = {
971+
if #available(macOS 11, *) {
972+
return NoAvailable(AfterDeploymentTarget())
973+
} else {
974+
return NoAvailable(AfterDeploymentTarget()) // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}} expected-note {{add 'if #available'}}
975+
}
976+
}(),
977+
bPublicInitAvailBetween: Any = {
978+
if #available(macOS 11, *) {
979+
return BeforeInliningTarget(AfterDeploymentTarget())
980+
} else {
981+
return BeforeInliningTarget(AfterDeploymentTarget()) // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}} expected-note {{add 'if #available'}}
982+
}
983+
}(),
984+
cPublicInitAvailBetween: Any = {
985+
if #available(macOS 11, *) {
986+
return AtInliningTarget(AfterDeploymentTarget())
987+
} else {
988+
return AtInliningTarget(AfterDeploymentTarget()) // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}} expected-note {{add 'if #available'}}
989+
}
990+
}(),
991+
dPublicInitAvailBetween: Any = {
992+
if #available(macOS 11, *) {
993+
return BetweenTargets(AfterDeploymentTarget())
994+
} else {
995+
return BetweenTargets(AfterDeploymentTarget()) // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}} expected-note {{add 'if #available'}}
996+
}
997+
}(),
998+
ePublicInitAvailBetween: Any = {
999+
if #available(macOS 11, *) {
1000+
return AtDeploymentTarget(AfterDeploymentTarget())
1001+
} else {
1002+
return AtDeploymentTarget(AfterDeploymentTarget()) // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}} expected-note {{add 'if #available'}}
1003+
}
1004+
}(),
1005+
fPublicInitAvailBetween: Any = {
1006+
if #available(macOS 11, *) {
1007+
return AfterDeploymentTarget()
1008+
} else {
1009+
return AfterDeploymentTarget() // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}} expected-note {{add 'if #available'}}
1010+
}
1011+
}()
1012+
9621013
// Internal declarations are not exposed.
9631014
var aInternal: NoAvailable = .init(),
9641015
bInternal: BeforeInliningTarget = .init(),

0 commit comments

Comments
 (0)