Skip to content

Commit 716cc5c

Browse files
authored
Merge pull request #79249 from tshortli/availability-constraints
AST: Begin consolidating availability constraint queries
2 parents ea99204 + 862dccf commit 716cc5c

File tree

8 files changed

+346
-63
lines changed

8 files changed

+346
-63
lines changed

include/swift/AST/AvailabilityConstraint.h

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#ifndef SWIFT_AST_AVAILABILITY_CONSTRAINT_H
1818
#define SWIFT_AST_AVAILABILITY_CONSTRAINT_H
1919

20+
#include "swift/AST/Attr.h"
2021
#include "swift/AST/AvailabilityDomain.h"
2122
#include "swift/AST/AvailabilityRange.h"
2223
#include "swift/AST/PlatformKind.h"
@@ -25,7 +26,8 @@
2526
namespace swift {
2627

2728
class ASTContext;
28-
class AvailableAttr;
29+
class AvailabilityContext;
30+
class Decl;
2931

3032
/// Represents the reason a declaration could be considered unavailable in a
3133
/// certain context.
@@ -110,6 +112,37 @@ class AvailabilityConstraint {
110112
bool isActiveForRuntimeQueries(ASTContext &ctx) const;
111113
};
112114

115+
/// Represents a set of availability constraints that restrict use of a
116+
/// declaration in a particular context.
117+
class DeclAvailabilityConstraints {
118+
using Storage = llvm::SmallVector<AvailabilityConstraint, 4>;
119+
Storage constraints;
120+
121+
public:
122+
DeclAvailabilityConstraints() {}
123+
124+
void addConstraint(const AvailabilityConstraint &constraint) {
125+
constraints.emplace_back(constraint);
126+
}
127+
128+
using const_iterator = Storage::const_iterator;
129+
const_iterator begin() const { return constraints.begin(); }
130+
const_iterator end() const { return constraints.end(); }
131+
};
132+
133+
/// Returns the `AvailabilityConstraint` that describes how \p attr restricts
134+
/// use of \p decl in \p context or `std::nullopt` if there is no restriction.
135+
std::optional<AvailabilityConstraint>
136+
getAvailabilityConstraintForAttr(const Decl *decl,
137+
const SemanticAvailableAttr &attr,
138+
const AvailabilityContext &context);
139+
140+
/// Returns the set of availability constraints that restrict use of \p decl
141+
/// when it is referenced from the given context. In other words, it is the
142+
/// collection of of `@available` attributes with unsatisfied conditions.
143+
DeclAvailabilityConstraints
144+
getAvailabilityConstraintsForDecl(const Decl *decl,
145+
const AvailabilityContext &context);
113146
} // end namespace swift
114147

115148
#endif

include/swift/AST/AvailabilityContextStorage.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
namespace swift {
2525

26+
class DeclAvailabilityConstraints;
27+
2628
/// Summarizes availability the constraints contained by an AvailabilityContext.
2729
class AvailabilityContext::Info {
2830
public:
@@ -41,9 +43,11 @@ class AvailabilityContext::Info {
4143
/// of adding this constraint.
4244
bool constrainWith(const Info &other);
4345

44-
/// Updates each field to reflect the availability of `decl`, if that
45-
/// availability is more restrictive. Returns true if any field was updated.
46-
bool constrainWith(const Decl *decl);
46+
/// Constrains each field using the given constraints if they are more
47+
/// restrictive than the current values. Returns true if any field was
48+
/// updated.
49+
bool constrainWith(const DeclAvailabilityConstraints &constraints,
50+
ASTContext &ctx);
4751

4852
bool constrainUnavailability(std::optional<AvailabilityDomain> domain);
4953

lib/AST/Availability.cpp

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
#include "swift/AST/ASTContext.h"
1818
#include "swift/AST/Attr.h"
19-
#include "swift/AST/AvailabilityConstraint.h"
2019
#include "swift/AST/AvailabilityDomain.h"
2120
#include "swift/AST/AvailabilityInference.h"
2221
#include "swift/AST/AvailabilityRange.h"
@@ -67,43 +66,6 @@ AvailabilityRange AvailabilityRange::forRuntimeTarget(const ASTContext &Ctx) {
6766
return AvailabilityRange(VersionRange::allGTE(Ctx.LangOpts.RuntimeVersion));
6867
}
6968

70-
PlatformKind AvailabilityConstraint::getPlatform() const {
71-
return getAttr().getPlatform();
72-
}
73-
74-
std::optional<AvailabilityRange>
75-
AvailabilityConstraint::getRequiredNewerAvailabilityRange(
76-
ASTContext &ctx) const {
77-
switch (getKind()) {
78-
case Kind::AlwaysUnavailable:
79-
case Kind::RequiresVersion:
80-
case Kind::Obsoleted:
81-
return std::nullopt;
82-
case Kind::IntroducedInNewerVersion:
83-
return getAttr().getIntroducedRange(ctx);
84-
}
85-
}
86-
87-
bool AvailabilityConstraint::isConditionallySatisfiable() const {
88-
switch (getKind()) {
89-
case Kind::AlwaysUnavailable:
90-
case Kind::RequiresVersion:
91-
case Kind::Obsoleted:
92-
return false;
93-
case Kind::IntroducedInNewerVersion:
94-
return true;
95-
}
96-
}
97-
98-
bool AvailabilityConstraint::isActiveForRuntimeQueries(ASTContext &ctx) const {
99-
if (getAttr().getPlatform() == PlatformKind::none)
100-
return true;
101-
102-
return swift::isPlatformActive(getAttr().getPlatform(), ctx.LangOpts,
103-
/*forTargetVariant=*/false,
104-
/*forRuntimeQuery=*/true);
105-
}
106-
10769
namespace {
10870

10971
/// The inferred availability required to access a group of declarations

lib/AST/AvailabilityConstraint.cpp

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
//===--- AvailabilityConstraint.cpp - Swift Availability Constraints ------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "swift/AST/AvailabilityConstraint.h"
14+
#include "swift/AST/ASTContext.h"
15+
#include "swift/AST/AvailabilityContext.h"
16+
#include "swift/AST/AvailabilityInference.h"
17+
#include "swift/AST/Decl.h"
18+
19+
using namespace swift;
20+
21+
PlatformKind AvailabilityConstraint::getPlatform() const {
22+
return getAttr().getPlatform();
23+
}
24+
25+
std::optional<AvailabilityRange>
26+
AvailabilityConstraint::getRequiredNewerAvailabilityRange(
27+
ASTContext &ctx) const {
28+
switch (getKind()) {
29+
case Kind::AlwaysUnavailable:
30+
case Kind::RequiresVersion:
31+
case Kind::Obsoleted:
32+
return std::nullopt;
33+
case Kind::IntroducedInNewerVersion:
34+
return getAttr().getIntroducedRange(ctx);
35+
}
36+
}
37+
38+
bool AvailabilityConstraint::isConditionallySatisfiable() const {
39+
switch (getKind()) {
40+
case Kind::AlwaysUnavailable:
41+
case Kind::RequiresVersion:
42+
case Kind::Obsoleted:
43+
return false;
44+
case Kind::IntroducedInNewerVersion:
45+
return true;
46+
}
47+
}
48+
49+
bool AvailabilityConstraint::isActiveForRuntimeQueries(ASTContext &ctx) const {
50+
if (getAttr().getPlatform() == PlatformKind::none)
51+
return true;
52+
53+
return swift::isPlatformActive(getAttr().getPlatform(), ctx.LangOpts,
54+
/*forTargetVariant=*/false,
55+
/*forRuntimeQuery=*/true);
56+
}
57+
58+
static bool
59+
isInsideCompatibleUnavailableDeclaration(const Decl *decl,
60+
const SemanticAvailableAttr &attr,
61+
const AvailabilityContext &context) {
62+
if (!context.isUnavailable())
63+
return false;
64+
65+
if (!attr.isUnconditionallyUnavailable())
66+
return false;
67+
68+
// Refuse calling universally unavailable functions from unavailable code,
69+
// but allow the use of types.
70+
auto domain = attr.getDomain();
71+
if (!isa<TypeDecl>(decl) && !isa<ExtensionDecl>(decl)) {
72+
if (domain.isUniversal() || domain.isSwiftLanguage())
73+
return false;
74+
}
75+
76+
return context.containsUnavailableDomain(domain);
77+
}
78+
79+
std::optional<AvailabilityConstraint>
80+
swift::getAvailabilityConstraintForAttr(const Decl *decl,
81+
const SemanticAvailableAttr &attr,
82+
const AvailabilityContext &context) {
83+
if (isInsideCompatibleUnavailableDeclaration(decl, attr, context))
84+
return std::nullopt;
85+
86+
if (attr.isUnconditionallyUnavailable())
87+
return AvailabilityConstraint::forAlwaysUnavailable(attr);
88+
89+
auto &ctx = decl->getASTContext();
90+
auto deploymentVersion = attr.getActiveVersion(ctx);
91+
auto deploymentRange =
92+
AvailabilityRange(VersionRange::allGTE(deploymentVersion));
93+
std::optional<llvm::VersionTuple> obsoletedVersion = attr.getObsoleted();
94+
95+
{
96+
StringRef obsoletedPlatform;
97+
llvm::VersionTuple remappedObsoletedVersion;
98+
if (AvailabilityInference::updateObsoletedPlatformForFallback(
99+
attr, ctx, obsoletedPlatform, remappedObsoletedVersion))
100+
obsoletedVersion = remappedObsoletedVersion;
101+
}
102+
103+
if (obsoletedVersion && *obsoletedVersion <= deploymentVersion)
104+
return AvailabilityConstraint::forObsoleted(attr);
105+
106+
AvailabilityRange introducedRange = attr.getIntroducedRange(ctx);
107+
108+
// FIXME: [availability] Expand this to cover custom versioned domains
109+
if (attr.isPlatformSpecific()) {
110+
if (!context.getPlatformRange().isContainedIn(introducedRange))
111+
return AvailabilityConstraint::forIntroducedInNewerVersion(attr);
112+
} else if (!deploymentRange.isContainedIn(introducedRange)) {
113+
return AvailabilityConstraint::forRequiresVersion(attr);
114+
}
115+
116+
return std::nullopt;
117+
}
118+
119+
/// Returns the most specific platform domain from the availability attributes
120+
/// attached to \p decl or `std::nullopt` if there are none. Platform specific
121+
/// `@available` attributes for other platforms should be ignored. For example,
122+
/// if a declaration has attributes for both iOS and macCatalyst, only the
123+
/// macCatalyst attributes take effect when compiling for a macCatalyst target.
124+
static std::optional<AvailabilityDomain>
125+
activePlatformDomainForDecl(const Decl *decl) {
126+
std::optional<AvailabilityDomain> activeDomain;
127+
for (auto attr :
128+
decl->getSemanticAvailableAttrs(/*includingInactive=*/false)) {
129+
auto domain = attr.getDomain();
130+
if (!domain.isPlatform())
131+
continue;
132+
133+
if (activeDomain && domain.contains(*activeDomain))
134+
continue;
135+
136+
activeDomain.emplace(domain);
137+
}
138+
139+
return activeDomain;
140+
}
141+
142+
static void
143+
getAvailabilityConstraintsForDecl(DeclAvailabilityConstraints &constraints,
144+
const Decl *decl,
145+
const AvailabilityContext &context) {
146+
auto activePlatformDomain = activePlatformDomainForDecl(decl);
147+
148+
for (auto attr :
149+
decl->getSemanticAvailableAttrs(/*includingInactive=*/false)) {
150+
auto domain = attr.getDomain();
151+
if (domain.isPlatform() && activePlatformDomain &&
152+
!activePlatformDomain->contains(domain))
153+
continue;
154+
155+
if (auto constraint =
156+
swift::getAvailabilityConstraintForAttr(decl, attr, context))
157+
constraints.addConstraint(*constraint);
158+
}
159+
}
160+
161+
DeclAvailabilityConstraints
162+
swift::getAvailabilityConstraintsForDecl(const Decl *decl,
163+
const AvailabilityContext &context) {
164+
DeclAvailabilityConstraints constraints;
165+
166+
// Generic parameters are always available.
167+
if (isa<GenericTypeParamDecl>(decl))
168+
return constraints;
169+
170+
decl = abstractSyntaxDeclForAvailableAttribute(decl);
171+
172+
getAvailabilityConstraintsForDecl(constraints, decl, context);
173+
174+
// If decl is an extension member, query the attributes of the extension, too.
175+
//
176+
// Skip decls imported from Clang, though, as they could be associated to the
177+
// wrong extension and inherit unavailability incorrectly. ClangImporter
178+
// associates Objective-C protocol members to the first category where the
179+
// protocol is directly or indirectly adopted, no matter its availability
180+
// and the availability of other categories. rdar://problem/53956555
181+
if (decl->getClangNode())
182+
return constraints;
183+
184+
auto parent = AvailabilityInference::parentDeclForInferredAvailability(decl);
185+
if (auto extension = dyn_cast_or_null<ExtensionDecl>(parent))
186+
getAvailabilityConstraintsForDecl(constraints, extension, context);
187+
188+
return constraints;
189+
}

lib/AST/AvailabilityContext.cpp

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "swift/AST/AvailabilityContext.h"
1414
#include "swift/AST/ASTContext.h"
15+
#include "swift/AST/AvailabilityConstraint.h"
1516
#include "swift/AST/AvailabilityContextStorage.h"
1617
#include "swift/AST/AvailabilityInference.h"
1718
#include "swift/AST/Decl.h"
@@ -63,16 +64,27 @@ bool AvailabilityContext::Info::constrainWith(const Info &other) {
6364
return isConstrained;
6465
}
6566

66-
bool AvailabilityContext::Info::constrainWith(const Decl *decl) {
67+
bool AvailabilityContext::Info::constrainWith(
68+
const DeclAvailabilityConstraints &constraints, ASTContext &ctx) {
6769
bool isConstrained = false;
6870

69-
if (auto range = AvailabilityInference::annotatedAvailableRange(decl))
70-
isConstrained |= constrainRange(Range, *range);
71-
72-
if (auto attr = decl->getUnavailableAttr())
73-
isConstrained |= constrainUnavailability(attr->getDomain());
74-
75-
isConstrained |= CONSTRAIN_BOOL(IsDeprecated, decl->isDeprecated());
71+
for (auto constraint : constraints) {
72+
auto attr = constraint.getAttr();
73+
auto domain = attr.getDomain();
74+
switch (constraint.getKind()) {
75+
case AvailabilityConstraint::Kind::AlwaysUnavailable:
76+
case AvailabilityConstraint::Kind::Obsoleted:
77+
case AvailabilityConstraint::Kind::RequiresVersion:
78+
isConstrained |= constrainUnavailability(domain);
79+
break;
80+
case AvailabilityConstraint::Kind::IntroducedInNewerVersion:
81+
// FIXME: [availability] Support versioning for other kinds of domains.
82+
DEBUG_ASSERT(domain.isPlatform());
83+
if (domain.isPlatform())
84+
isConstrained |= constrainRange(Range, attr.getIntroducedRange(ctx));
85+
break;
86+
}
87+
}
7688

7789
return isConstrained;
7890
}
@@ -190,7 +202,9 @@ void AvailabilityContext::constrainWithDeclAndPlatformRange(
190202
bool isConstrained = false;
191203

192204
Info info{storage->info};
193-
isConstrained |= info.constrainWith(decl);
205+
auto constraints = swift::getAvailabilityConstraintsForDecl(decl, *this);
206+
isConstrained |= info.constrainWith(constraints, decl->getASTContext());
207+
isConstrained |= CONSTRAIN_BOOL(info.IsDeprecated, decl->isDeprecated());
194208
isConstrained |= constrainRange(info.Range, platformRange);
195209

196210
if (!isConstrained)

lib/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ add_swift_host_library(swiftAST STATIC
2727
Attr.cpp
2828
AutoDiff.cpp
2929
Availability.cpp
30+
AvailabilityConstraint.cpp
3031
AvailabilityContext.cpp
3132
AvailabilityDomain.cpp
3233
AvailabilityScope.cpp

0 commit comments

Comments
 (0)