-
Notifications
You must be signed in to change notification settings - Fork 10.4k
/
Copy pathAvailabilityConstraint.cpp
248 lines (202 loc) · 9.02 KB
/
AvailabilityConstraint.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
//===--- AvailabilityConstraint.cpp - Swift Availability Constraints ------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/AST/AvailabilityConstraint.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/AvailabilityContext.h"
#include "swift/AST/AvailabilityInference.h"
#include "swift/AST/Decl.h"
using namespace swift;
std::optional<AvailabilityRange>
AvailabilityConstraint::getPotentiallyUnavailableRange(
const ASTContext &ctx) const {
switch (getReason()) {
case Reason::UnconditionallyUnavailable:
case Reason::Obsoleted:
case Reason::UnavailableForDeployment:
return std::nullopt;
case Reason::PotentiallyUnavailable:
return getAttr().getIntroducedRange(ctx);
}
}
bool AvailabilityConstraint::isActiveForRuntimeQueries(
const ASTContext &ctx) const {
if (getAttr().getPlatform() == PlatformKind::none)
return true;
return swift::isPlatformActive(getAttr().getPlatform(), ctx.LangOpts,
/*forTargetVariant=*/false,
/*forRuntimeQuery=*/true);
}
static bool constraintIsStronger(const AvailabilityConstraint &lhs,
const AvailabilityConstraint &rhs) {
DEBUG_ASSERT(lhs.getDomain() == rhs.getDomain());
// If the constraints have matching domains but different reasons, the
// constraint with the lowest reason is "strongest".
if (lhs.getReason() != rhs.getReason())
return lhs.getReason() < rhs.getReason();
switch (lhs.getReason()) {
case AvailabilityConstraint::Reason::UnconditionallyUnavailable:
// Just keep the first.
return false;
case AvailabilityConstraint::Reason::Obsoleted:
// Pick the larger obsoleted range.
return *lhs.getAttr().getObsoleted() < *rhs.getAttr().getObsoleted();
case AvailabilityConstraint::Reason::UnavailableForDeployment:
case AvailabilityConstraint::Reason::PotentiallyUnavailable:
// Pick the smaller introduced range.
return *lhs.getAttr().getIntroduced() > *rhs.getAttr().getIntroduced();
}
}
void addConstraint(llvm::SmallVector<AvailabilityConstraint, 4> &constraints,
const AvailabilityConstraint &constraint,
const ASTContext &ctx) {
auto iter = llvm::find_if(
constraints, [&constraint](AvailabilityConstraint &existing) {
return constraint.getDomain() == existing.getDomain();
});
// There's no existing constraint for the same domain so just add it.
if (iter == constraints.end()) {
constraints.emplace_back(constraint);
return;
}
if (constraintIsStronger(constraint, *iter)) {
constraints.erase(iter);
constraints.emplace_back(constraint);
}
}
std::optional<AvailabilityConstraint>
DeclAvailabilityConstraints::getPrimaryConstraint() const {
std::optional<AvailabilityConstraint> result;
auto isStrongerConstraint = [](const AvailabilityConstraint &lhs,
const AvailabilityConstraint &rhs) {
// Constraint reasons are defined in descending order of strength.
if (lhs.getReason() != rhs.getReason())
return lhs.getReason() < rhs.getReason();
// Pick the constraint from the broader domain.
if (lhs.getDomain() != rhs.getDomain())
return rhs.getDomain().contains(lhs.getDomain());
return false;
};
// Pick the strongest constraint.
for (auto const &constraint : constraints) {
if (!result || isStrongerConstraint(constraint, *result))
result.emplace(constraint);
}
return result;
}
static bool
isInsideCompatibleUnavailableDeclaration(const Decl *decl,
const SemanticAvailableAttr &attr,
const AvailabilityContext &context) {
if (!context.isUnavailable())
return false;
if (!attr.isUnconditionallyUnavailable())
return false;
// Refuse calling universally unavailable functions from unavailable code,
// but allow the use of types.
auto domain = attr.getDomain();
if (!isa<TypeDecl>(decl) && !isa<ExtensionDecl>(decl)) {
if (domain.isUniversal() || domain.isSwiftLanguage())
return false;
}
return context.containsUnavailableDomain(domain);
}
/// Returns the `AvailabilityConstraint` that describes how \p attr restricts
/// use of \p decl in \p context or `std::nullopt` if there is no restriction.
static std::optional<AvailabilityConstraint>
getAvailabilityConstraintForAttr(const Decl *decl,
const SemanticAvailableAttr &attr,
const AvailabilityContext &context) {
if (attr.isUnconditionallyUnavailable())
return AvailabilityConstraint::unconditionallyUnavailable(attr);
auto &ctx = decl->getASTContext();
auto deploymentRange = attr.getDomain().getDeploymentRange(ctx);
auto obsoletedRange = attr.getObsoletedRange(ctx);
if (deploymentRange && deploymentRange->isContainedIn(obsoletedRange))
return AvailabilityConstraint::obsoleted(attr);
AvailabilityRange introducedRange = attr.getIntroducedRange(ctx);
// FIXME: [availability] Expand this to cover custom versioned domains
if (attr.isPlatformSpecific()) {
if (!context.getPlatformRange().isContainedIn(introducedRange))
return AvailabilityConstraint::potentiallyUnavailable(attr);
} else if (deploymentRange &&
!deploymentRange->isContainedIn(introducedRange)) {
return AvailabilityConstraint::unavailableForDeployment(attr);
}
return std::nullopt;
}
/// Returns the most specific platform domain from the availability attributes
/// attached to \p decl or `std::nullopt` if there are none. Platform specific
/// `@available` attributes for other platforms should be ignored. For example,
/// if a declaration has attributes for both iOS and macCatalyst, only the
/// macCatalyst attributes take effect when compiling for a macCatalyst target.
static std::optional<AvailabilityDomain>
activePlatformDomainForDecl(const Decl *decl) {
std::optional<AvailabilityDomain> activeDomain;
for (auto attr :
decl->getSemanticAvailableAttrs(/*includingInactive=*/false)) {
auto domain = attr.getDomain();
if (!domain.isPlatform())
continue;
if (activeDomain && domain.contains(*activeDomain))
continue;
activeDomain.emplace(domain);
}
return activeDomain;
}
static void getAvailabilityConstraintsForDecl(
llvm::SmallVector<AvailabilityConstraint, 4> &constraints, const Decl *decl,
const AvailabilityContext &context) {
auto &ctx = decl->getASTContext();
auto activePlatformDomain = activePlatformDomainForDecl(decl);
for (auto attr :
decl->getSemanticAvailableAttrs(/*includingInactive=*/false)) {
auto domain = attr.getDomain();
if (domain.isPlatform() && activePlatformDomain &&
!activePlatformDomain->contains(domain))
continue;
if (auto constraint = getAvailabilityConstraintForAttr(decl, attr, context))
addConstraint(constraints, *constraint, ctx);
}
// After resolving constraints, remove any constraints that indicate the
// declaration is unconditionally unavailable in a domain for which
// the context is already unavailable.
llvm::erase_if(constraints, [&](const AvailabilityConstraint &constraint) {
return isInsideCompatibleUnavailableDeclaration(decl, constraint.getAttr(),
context);
});
}
DeclAvailabilityConstraints
swift::getAvailabilityConstraintsForDecl(const Decl *decl,
const AvailabilityContext &context,
AvailabilityConstraintFlags flags) {
llvm::SmallVector<AvailabilityConstraint, 4> constraints;
// Generic parameters are always available.
if (isa<GenericTypeParamDecl>(decl))
return DeclAvailabilityConstraints();
decl = abstractSyntaxDeclForAvailableAttribute(decl);
getAvailabilityConstraintsForDecl(constraints, decl, context);
if (flags.contains(AvailabilityConstraintFlag::SkipEnclosingExtension))
return constraints;
// If decl is an extension member, query the attributes of the extension, too.
//
// Skip decls imported from Clang, though, as they could be associated to the
// wrong extension and inherit unavailability incorrectly. ClangImporter
// associates Objective-C protocol members to the first category where the
// protocol is directly or indirectly adopted, no matter its availability
// and the availability of other categories. rdar://problem/53956555
if (decl->getClangNode())
return constraints;
auto parent = AvailabilityInference::parentDeclForInferredAvailability(decl);
if (auto extension = dyn_cast_or_null<ExtensionDecl>(parent))
getAvailabilityConstraintsForDecl(constraints, extension, context);
return constraints;
}