Skip to content

Commit d1b2439

Browse files
authored
Merge pull request #59475 from tshortli/build-default-argument-expr-trcs
Sema: Fix availability checking in function default argument closure expressions
2 parents 66d7133 + 4230a66 commit d1b2439

File tree

2 files changed

+112
-11
lines changed

2 files changed

+112
-11
lines changed

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -356,21 +356,34 @@ namespace {
356356
/// A class to walk the AST to build the type refinement context hierarchy.
357357
class TypeRefinementContextBuilder : private ASTWalker {
358358

359+
ASTContext &Context;
360+
361+
/// Represents an entry in a stack of active type refinement contexts. The
362+
/// stack is used to facilitate building the TRC's tree structure. A new TRC
363+
/// is pushed onto this stack before visiting children whenever the current
364+
/// AST node requires a new context and the TRC is then popped
365+
/// post-visitation.
359366
struct ContextInfo {
360367
TypeRefinementContext *TRC;
361368

362-
/// The node whose end marks the end of the refinement context.
363-
/// If the builder sees this node in a post-visitor, it will pop
364-
/// the context from the stack. This node can be null (ParentTy()),
369+
/// The AST node. This node can be null (ParentTy()),
365370
/// indicating that custom logic elsewhere will handle removing
366371
/// the context when needed.
367372
ParentTy ScopeNode;
368373

369374
bool ContainedByDeploymentTarget;
370375
};
371-
372376
std::vector<ContextInfo> ContextStack;
373-
ASTContext &Context;
377+
378+
/// Represents an entry in a stack of pending decl body type refinement
379+
/// contexts. TRCs in this stack should be pushed onto \p ContextStack when
380+
/// \p BodyStmt is encountered.
381+
struct DeclBodyContextInfo {
382+
TypeRefinementContext *TRC;
383+
Decl *Decl;
384+
Stmt *BodyStmt;
385+
};
386+
std::vector<DeclBodyContextInfo> DeclBodyContextStack;
374387

375388
/// A mapping from abstract storage declarations with accessors to
376389
/// to the type refinement contexts for those declarations. We refer to
@@ -411,6 +424,15 @@ class TypeRefinementContextBuilder : private ASTWalker {
411424
ContextStack.push_back(Info);
412425
}
413426

427+
void pushDeclBodyContext(TypeRefinementContext *TRC, Decl *D, Stmt *S) {
428+
DeclBodyContextInfo Info;
429+
Info.TRC = TRC;
430+
Info.Decl = D;
431+
Info.BodyStmt = S;
432+
433+
DeclBodyContextStack.push_back(Info);
434+
}
435+
414436
const char *stackTraceAction() const {
415437
return "building type refinement context for";
416438
}
@@ -474,6 +496,12 @@ class TypeRefinementContextBuilder : private ASTWalker {
474496
while (ContextStack.back().ScopeNode.getAsDecl() == D) {
475497
ContextStack.pop_back();
476498
}
499+
500+
while (!DeclBodyContextStack.empty() &&
501+
DeclBodyContextStack.back().Decl == D) {
502+
DeclBodyContextStack.pop_back();
503+
}
504+
477505
return true;
478506
}
479507

@@ -689,18 +717,22 @@ class TypeRefinementContextBuilder : private ASTWalker {
689717

690718
// Top level code always uses the deployment target.
691719
if (auto tlcd = dyn_cast<TopLevelCodeDecl>(D)) {
692-
auto *topLevelTRC = createContext(tlcd, tlcd->getSourceRange());
693-
pushContext(topLevelTRC, D);
720+
if (auto bodyStmt = tlcd->getBody()) {
721+
pushDeclBodyContext(createContext(tlcd, tlcd->getSourceRange()), tlcd,
722+
bodyStmt);
723+
}
694724
return;
695725
}
696726

697727
// Function bodies use the deployment target if they are within the module's
698728
// resilience domain.
699729
if (auto afd = dyn_cast<AbstractFunctionDecl>(D)) {
700-
if (!afd->isImplicit() && afd->getBodySourceRange().isValid() &&
730+
if (!afd->isImplicit() &&
701731
afd->getResilienceExpansion() != ResilienceExpansion::Minimal) {
702-
auto *functionBodyTRC = createContext(afd, afd->getBodySourceRange());
703-
pushContext(functionBodyTRC, D);
732+
if (auto body = afd->getBody(/*canSynthesize*/ false)) {
733+
pushDeclBodyContext(createContext(afd, afd->getBodySourceRange()),
734+
afd, body);
735+
}
704736
}
705737
return;
706738
}
@@ -748,6 +780,10 @@ class TypeRefinementContextBuilder : private ASTWalker {
748780
std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
749781
PrettyStackTraceStmt trace(Context, stackTraceAction(), S);
750782

783+
if (consumeDeclBodyContextIfNecessary(S)) {
784+
return std::make_pair(true, S);
785+
}
786+
751787
if (auto *IS = dyn_cast<IfStmt>(S)) {
752788
buildIfStmtRefinementContext(IS);
753789
return std::make_pair(false, S);
@@ -778,6 +814,22 @@ class TypeRefinementContextBuilder : private ASTWalker {
778814
return S;
779815
}
780816

817+
/// Consumes the top TRC from \p DeclBodyContextStack and pushes it onto the
818+
/// \p Context stack if the given \p Stmt is the matching body statement.
819+
/// Returns \p true if a context was pushed.
820+
bool consumeDeclBodyContextIfNecessary(Stmt *S) {
821+
if (DeclBodyContextStack.empty())
822+
return false;
823+
824+
auto Info = DeclBodyContextStack.back();
825+
if (S != Info.BodyStmt)
826+
return false;
827+
828+
pushContext(Info.TRC, Info.BodyStmt);
829+
DeclBodyContextStack.pop_back();
830+
return true;
831+
}
832+
781833
/// Builds the type refinement hierarchy for the IfStmt if the guard
782834
/// introduces a new refinement context for the Then branch.
783835
/// There is no need for the caller to explicitly traverse the children

test/attr/attr_inlinable_available.swift

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ public func backDeployedToInliningTarget(
655655

656656
// MARK: - Default arguments
657657

658-
// Default arguments act like @inlinable.
658+
// Default arguments act like @inlinable when in a public function.
659659

660660
public func defaultArgsUseNoAvailable( // expected-note 3 {{add @available attribute}}
661661
_: Any = NoAvailable.self,
@@ -666,6 +666,15 @@ public func defaultArgsUseNoAvailable( // expected-note 3 {{add @available attri
666666
_: Any = AfterDeploymentTarget.self // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}}
667667
) {}
668668

669+
func defaultArgsUseInternal( // expected-note {{add @available attribute}}
670+
_: Any = NoAvailable.self,
671+
_: Any = BeforeInliningTarget.self,
672+
_: Any = AtInliningTarget.self,
673+
_: Any = BetweenTargets.self,
674+
_: Any = AtDeploymentTarget.self,
675+
_: Any = AfterDeploymentTarget.self // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}}
676+
) {}
677+
669678
@available(macOS, unavailable)
670679
public func defaultArgsUseUnavailable(
671680
_: Any = NoAvailable.self,
@@ -687,6 +696,46 @@ public func spiDefaultArgsUseNoAvailable( // expected-note 1 {{add @available at
687696
_: Any = AfterDeploymentTarget.self // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}}
688697
) {}
689698

699+
// Verify that complex default argument expressions are checked appropriately.
700+
public func defaultArgsClosureExprNoAvailable( // expected-note 3 {{add @available attribute}}
701+
_: Int = {
702+
_ = NoAvailable.self
703+
_ = BeforeInliningTarget.self
704+
_ = AtInliningTarget.self
705+
_ = BetweenTargets.self // expected-error {{'BetweenTargets' is only available in macOS 10.14.5 or newer; clients of 'Test' may have a lower deployment target}} expected-note {{add 'if #available' version check}}
706+
_ = AtDeploymentTarget.self // expected-error {{'AtDeploymentTarget' is only available in macOS 10.15 or newer; clients of 'Test' may have a lower deployment target}} expected-note {{add 'if #available' version check}}
707+
_ = AfterDeploymentTarget.self // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}} expected-note {{add 'if #available' version check}}
708+
if #available(macOS 10.14.5, *) {
709+
_ = BetweenTargets.self
710+
}
711+
if #available(macOS 10.15, *) {
712+
_ = AtDeploymentTarget.self
713+
}
714+
if #available(macOS 11, *) {
715+
_ = AfterDeploymentTarget.self
716+
}
717+
return 42
718+
}()
719+
) {}
720+
721+
func defaultArgsClosureExprInternal( // expected-note {{add @available attribute}}
722+
_: Int = {
723+
_ = NoAvailable.self
724+
_ = BeforeInliningTarget.self
725+
_ = AtInliningTarget.self
726+
_ = BetweenTargets.self
727+
_ = AtDeploymentTarget.self
728+
_ = AfterDeploymentTarget.self // expected-error {{'AfterDeploymentTarget' is only available in macOS 11 or newer}} expected-note {{add 'if #available' version check}}
729+
if #available(macOS 11, *) {
730+
_ = AfterDeploymentTarget.self
731+
}
732+
return 42
733+
}()
734+
) {}
735+
736+
737+
// MARK: - Properties
738+
690739
@propertyWrapper
691740
public struct PropertyWrapper<T> {
692741
public var wrappedValue: T

0 commit comments

Comments
 (0)