Skip to content

Commit 722cb83

Browse files
committed
Extend transitive availability checking to initial value expressions
Because initial value expressions aren't actually considered /within/ the VarDecl or PatternBindingDecl they're initializing, the existing logic to search for availability attributes wasn't kicking in, leading to errors when a conditionally-unavailable value was used in an initial value expression for a conditionally-unavailable binding. Fix this by walking the enclosing type or extension to find the appropriate PatternBindingDecl. https://bugs.swift.org/browse/SR-9867
1 parent ca0aeb2 commit 722cb83

File tree

2 files changed

+127
-45
lines changed

2 files changed

+127
-45
lines changed

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 40 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "TypeChecker.h"
1919
#include "MiscDiagnostics.h"
2020
#include "swift/AST/ASTWalker.h"
21+
#include "swift/AST/Initializer.h"
2122
#include "swift/AST/NameLookup.h"
2223
#include "swift/AST/Pattern.h"
2324
#include "swift/AST/TypeRefinementContext.h"
@@ -856,27 +857,41 @@ static Optional<ASTNode> findInnermostAncestor(
856857
static const Decl *findContainingDeclaration(SourceRange ReferenceRange,
857858
const DeclContext *ReferenceDC,
858859
const SourceManager &SM) {
859-
if (const Decl *D = ReferenceDC->getInnermostDeclarationDeclContext())
860+
auto ContainsReferenceRange = [&](const Decl *D) -> bool {
861+
if (ReferenceRange.isInvalid())
862+
return false;
863+
return SM.rangeContains(D->getSourceRange(), ReferenceRange);
864+
};
865+
866+
if (const Decl *D = ReferenceDC->getInnermostDeclarationDeclContext()) {
867+
// If we have an inner declaration context, see if we can narrow the search
868+
// down to one of its members. This is important for properties, which don't
869+
// count as DeclContexts of their own but which can still introduce
870+
// availability.
871+
if (auto *IDC = dyn_cast<IterableDeclContext>(D)) {
872+
auto BestMember = llvm::find_if(IDC->getMembers(),
873+
ContainsReferenceRange);
874+
if (BestMember != IDC->getMembers().end())
875+
return *BestMember;
876+
}
860877
return D;
878+
}
861879

862-
// We couldn't find a suitable node by climbing the DeclContext
863-
// hierarchy, so fall back to looking for a top-level declaration
864-
// that contains the reference range. We will hit this case for
865-
// top-level elements that do not themselves introduce DeclContexts,
866-
// such as extensions and global variables. If we don't have a reference
867-
// range, there is nothing we can do, so return null.
880+
// We couldn't find a suitable node by climbing the DeclContext hierarchy, so
881+
// fall back to looking for a top-level declaration that contains the
882+
// reference range. We will hit this case for top-level elements that do not
883+
// themselves introduce DeclContexts, such as global variables. If we don't
884+
// have a reference range, there is nothing we can do, so return null.
868885
if (ReferenceRange.isInvalid())
869886
return nullptr;
870887

871888
SourceFile *SF = ReferenceDC->getParentSourceFile();
872889
if (!SF)
873890
return nullptr;
874891

875-
for (Decl *D : SF->Decls) {
876-
if (SM.rangeContains(D->getSourceRange(), ReferenceRange)) {
877-
return D;
878-
}
879-
}
892+
auto BestTopLevelDecl = llvm::find_if(SF->Decls, ContainsReferenceRange);
893+
if (BestTopLevelDecl != SF->Decls.end())
894+
return *BestTopLevelDecl;
880895

881896
return nullptr;
882897
}
@@ -929,9 +944,13 @@ abstractSyntaxDeclForAvailableAttribute(const Decl *ConcreteSyntaxDecl) {
929944
// binding.
930945
ArrayRef<PatternBindingEntry> Entries = PBD->getPatternList();
931946
if (!Entries.empty()) {
932-
VarDecl *VD = Entries.front().getPattern()->getSingleVar();
933-
if (VD)
934-
return VD;
947+
const VarDecl *AnyVD = nullptr;
948+
// FIXME: This is wasteful; we only need the first variable.
949+
Entries.front().getPattern()->forEachVariable([&](const VarDecl *VD) {
950+
AnyVD = VD;
951+
});
952+
if (AnyVD)
953+
return AnyVD;
935954
}
936955
} else if (auto *ECD = dyn_cast<EnumCaseDecl>(ConcreteSyntaxDecl)) {
937956
// Similar to the PatternBindingDecl case above, we return the
@@ -1408,7 +1427,7 @@ someEnclosingDeclMatches(SourceRange ReferenceRange,
14081427
// Climb the DeclContext hierarchy to see if any of the containing
14091428
// declarations matches the predicate.
14101429
const DeclContext *DC = ReferenceDC;
1411-
do {
1430+
while (true) {
14121431
auto *D = DC->getInnermostDeclarationDeclContext();
14131432
if (!D)
14141433
break;
@@ -1424,20 +1443,15 @@ someEnclosingDeclMatches(SourceRange ReferenceRange,
14241443
return true;
14251444
}
14261445

1427-
DC = DC->getParent();
1428-
} while (DC);
1446+
DC = D->getDeclContext();
1447+
}
14291448

14301449
// Search the AST starting from our innermost declaration context to see if
14311450
// if the reference is inside a property declaration but not inside an
14321451
// accessor (this can happen for the TypeRepr for the declared type of a
14331452
// property, for example).
14341453
// We can't rely on the DeclContext hierarchy climb above because properties
1435-
// do not introduce a new DeclContext. This search is potentially slow, so we
1436-
// do it last and only if the reference declaration context is a
1437-
// type or global context.
1438-
1439-
if (!ReferenceDC->isTypeContext() && !ReferenceDC->isModuleScopeContext())
1440-
return false;
1454+
// do not introduce a new DeclContext.
14411455

14421456
// Don't search for a containing declaration if we don't have a source range.
14431457
if (ReferenceRange.isInvalid())
@@ -1448,28 +1462,11 @@ someEnclosingDeclMatches(SourceRange ReferenceRange,
14481462
findContainingDeclaration(ReferenceRange, ReferenceDC, Ctx.SourceMgr);
14491463

14501464
// We may not be able to find a declaration to search if the ReferenceRange
1451-
// is invalid (i.e., we are in synthesized code).
1465+
// isn't useful (i.e., we are in synthesized code).
14521466
if (!DeclToSearch)
14531467
return false;
14541468

1455-
InnermostAncestorFinder::MatchPredicate IsDeclaration =
1456-
[](ASTNode Node, ASTWalker::ParentTy Parent) {
1457-
return Node.is<Decl *>();
1458-
};
1459-
1460-
Optional<ASTNode> FoundDeclarationNode =
1461-
findInnermostAncestor(ReferenceRange, Ctx.SourceMgr,
1462-
const_cast<Decl *>(DeclToSearch), IsDeclaration);
1463-
1464-
if (FoundDeclarationNode.hasValue()) {
1465-
const Decl *D = FoundDeclarationNode.getValue().get<Decl *>();
1466-
D = abstractSyntaxDeclForAvailableAttribute(D);
1467-
if (Pred(D)) {
1468-
return true;
1469-
}
1470-
}
1471-
1472-
return false;
1469+
return Pred(abstractSyntaxDeclForAvailableAttribute(DeclToSearch));
14731470
}
14741471

14751472
/// Returns true if the reference or any of its parents is an

test/attr/attr_availability_transitive_osx.swift

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1-
// RUN: %target-typecheck-verify-swift
1+
// RUN: %target-typecheck-verify-swift -parse-as-library
22
// REQUIRES: OS=macosx
33

44
// Allow referencing unavailable API in situations where the caller is marked unavailable in the same circumstances.
55

66
@available(OSX, unavailable)
7-
func osx() {} // expected-note 2{{'osx()' has been explicitly marked unavailable here}}
7+
@discardableResult
8+
func osx() -> Int { return 0 } // expected-note * {{'osx()' has been explicitly marked unavailable here}}
89

910
@available(OSXApplicationExtension, unavailable)
1011
func osx_extension() {}
1112

13+
@available(OSX, unavailable)
14+
func osx_pair() -> (Int, Int) { return (0, 0) } // expected-note * {{'osx_pair()' has been explicitly marked unavailable here}}
15+
1216
func call_osx_extension() {
1317
osx_extension() // OK; osx_extension is only unavailable if -application-extension is passed.
1418
}
@@ -35,3 +39,84 @@ func osx_extension_call_osx_extension() {
3539
func osx_extension_call_osx() {
3640
osx() // expected-error {{'osx()' is unavailable}}
3741
}
42+
43+
@available(OSX, unavailable)
44+
var osx_init_osx = osx() // OK
45+
46+
@available(OSXApplicationExtension, unavailable)
47+
var osx_extension_init_osx = osx() // expected-error {{'osx()' is unavailable}}
48+
49+
@available(OSX, unavailable)
50+
var osx_inner_init_osx = { let inner_var = osx() } // OK
51+
52+
// FIXME: I'm not sure why this produces two errors instead of just one.
53+
@available(OSXApplicationExtension, unavailable)
54+
var osx_extension_inner_init_osx = { let inner_var = osx() } // expected-error 2 {{'osx()' is unavailable}}
55+
56+
struct Outer {
57+
@available(OSX, unavailable)
58+
var osx_init_osx = osx() // OK
59+
60+
@available(OSX, unavailable)
61+
lazy var osx_lazy_osx = osx() // OK
62+
63+
@available(OSXApplicationExtension, unavailable)
64+
var osx_extension_init_osx = osx() // expected-error {{'osx()' is unavailable}}
65+
66+
@available(OSXApplicationExtension, unavailable)
67+
var osx_extension_lazy_osx = osx() // expected-error {{'osx()' is unavailable}}
68+
69+
@available(OSX, unavailable)
70+
var osx_init_multi1_osx = osx(), osx_init_multi2_osx = osx() // OK
71+
72+
@available(OSXApplicationExtension, unavailable)
73+
var osx_extension_init_multi1_osx = osx(), osx_extension_init_multi2_osx = osx() // expected-error 2 {{'osx()' is unavailable}}
74+
75+
@available(OSX, unavailable)
76+
var (osx_init_deconstruct1_osx, osx_init_deconstruct2_osx) = osx_pair() // OK
77+
78+
@available(OSXApplicationExtension, unavailable)
79+
var (osx_extension_init_deconstruct1_osx, osx_extension_init_deconstruct2_osx) = osx_pair() // expected-error {{'osx_pair()' is unavailable}}
80+
81+
@available(OSX, unavailable)
82+
var (_, osx_init_deconstruct2_only_osx) = osx_pair() // OK
83+
84+
@available(OSXApplicationExtension, unavailable)
85+
var (_, osx_extension_init_deconstruct2_only_osx) = osx_pair() // expected-error {{'osx_pair()' is unavailable}}
86+
87+
@available(OSX, unavailable)
88+
var (osx_init_deconstruct1_only_osx, _) = osx_pair() // OK
89+
90+
@available(OSXApplicationExtension, unavailable)
91+
var (osx_extension_init_deconstruct1_only_osx, _) = osx_pair() // expected-error {{'osx_pair()' is unavailable}}
92+
93+
@available(OSX, unavailable)
94+
var osx_inner_init_osx = { let inner_var = osx() } // OK
95+
96+
// FIXME: I'm not sure why this produces two errors instead of just one.
97+
@available(OSXApplicationExtension, unavailable)
98+
var osx_extension_inner_init_osx = { let inner_var = osx() } // expected-error 2 {{'osx()' is unavailable}}
99+
}
100+
101+
extension Outer {
102+
@available(OSX, unavailable)
103+
static var also_osx_init_osx = osx() // OK
104+
105+
@available(OSXApplicationExtension, unavailable)
106+
static var also_osx_extension_init_osx = osx() // expected-error {{'osx()' is unavailable}}
107+
}
108+
109+
@available(OSX, unavailable)
110+
extension Outer {
111+
static var outer_osx_init_osx = osx() // OK
112+
}
113+
114+
@available(OSX, unavailable)
115+
struct NotOnOSX {
116+
var osx_init_osx = osx() // OK
117+
lazy var osx_lazy_osx = osx() // OK
118+
var osx_init_multi1_osx = osx(), osx_init_multi2_osx = osx() // OK
119+
var (osx_init_deconstruct1_osx, osx_init_deconstruct2_osx) = osx_pair() // OK
120+
var (_, osx_init_deconstruct2_only_osx) = osx_pair() // OK
121+
var (osx_init_deconstruct1_only_osx, _) = osx_pair() // OK
122+
}

0 commit comments

Comments
 (0)