Skip to content

Commit 62c8e72

Browse files
committed
AST: Centralize AvailabilityDomain lookup.
Implement lookup of availability domains for identifiers on `AvailabilityDomainOrIdentifier`. Add a bit to that type which represents whether or not lookup has already been attempted. This allows both `AvailableAttr` and `AvailabilitySpec` to share a common implementation of domain lookup.
1 parent 8ebf2aa commit 62c8e72

12 files changed

+127
-107
lines changed

include/swift/AST/Attr.h

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,7 @@ class AvailableAttr : public DeclAttribute {
746746

747747
private:
748748
friend class SemanticAvailableAttr;
749+
friend class SemanticAvailableAttrRequest;
749750

750751
AvailabilityDomainOrIdentifier DomainOrIdentifier;
751752
const SourceLoc DomainLoc;
@@ -769,13 +770,6 @@ class AvailableAttr : public DeclAttribute {
769770
return DomainOrIdentifier;
770771
}
771772

772-
/// Returns the `AvailabilityDomain` associated with the attribute, or
773-
/// `std::nullopt` if it has either not yet been resolved or could not be
774-
/// resolved successfully.
775-
std::optional<AvailabilityDomain> getCachedDomain() const {
776-
return DomainOrIdentifier.getAsDomain();
777-
}
778-
779773
SourceLoc getDomainLoc() const { return DomainLoc; }
780774

781775
/// Returns the parsed version for `introduced:`.
@@ -918,11 +912,6 @@ class AvailableAttr : public DeclAttribute {
918912
private:
919913
friend class SemanticAvailableAttrRequest;
920914

921-
void setCachedDomain(AvailabilityDomain domain) {
922-
assert(!DomainOrIdentifier.isDomain());
923-
DomainOrIdentifier.setDomain(domain);
924-
}
925-
926915
bool hasComputedSemanticAttr() const {
927916
return Bits.AvailableAttr.HasComputedSemanticAttr;
928917
}
@@ -3305,12 +3294,13 @@ class SemanticAvailableAttr final {
33053294
public:
33063295
SemanticAvailableAttr(const AvailableAttr *attr) : attr(attr) {
33073296
assert(attr);
3308-
assert(attr->hasCachedDomain());
3297+
bool hasDomain = attr->getDomainOrIdentifier().isDomain();
3298+
assert(hasDomain);
33093299
}
33103300

33113301
const AvailableAttr *getParsedAttr() const { return attr; }
33123302
const AvailabilityDomain getDomain() const {
3313-
return attr->getCachedDomain().value();
3303+
return attr->getDomainOrIdentifier().getAsDomain().value();
33143304
}
33153305

33163306
/// The version tuple for the `introduced:` component.

include/swift/AST/AvailabilityDomain.h

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "swift/AST/PlatformKind.h"
2424
#include "swift/Basic/Assertions.h"
2525
#include "swift/Basic/LLVM.h"
26+
#include "swift/Basic/SourceLoc.h"
2627
#include "llvm/ADT/FoldingSet.h"
2728
#include "llvm/ADT/PointerEmbeddedInt.h"
2829
#include "llvm/ADT/PointerUnion.h"
@@ -294,42 +295,65 @@ class CustomAvailabilityDomain : public ASTAllocated<CustomAvailabilityDomain> {
294295
class AvailabilityDomainOrIdentifier {
295296
friend struct llvm::PointerLikeTypeTraits<AvailabilityDomainOrIdentifier>;
296297

297-
using Storage = llvm::PointerUnion<AvailabilityDomain, Identifier>;
298+
using DomainOrIdentifier = llvm::PointerUnion<AvailabilityDomain, Identifier>;
299+
300+
/// Stores an extra bit representing whether the domain has been resolved.
301+
using Storage = llvm::PointerIntPair<DomainOrIdentifier, 1, bool>;
298302
Storage storage;
299303

300304
AvailabilityDomainOrIdentifier(Storage storage) : storage(storage) {}
301305

306+
static AvailabilityDomainOrIdentifier fromOpaque(void *opaque) {
307+
return AvailabilityDomainOrIdentifier(Storage::getFromOpaqueValue(opaque));
308+
}
309+
310+
std::optional<AvailabilityDomain>
311+
lookUpInDeclContext(SourceLoc loc, const DeclContext *declContext) const;
312+
302313
public:
303314
AvailabilityDomainOrIdentifier(Identifier identifier)
304315
: storage(identifier) {};
305316
AvailabilityDomainOrIdentifier(AvailabilityDomain domain)
306317
: storage(domain) {};
307318

308-
static AvailabilityDomainOrIdentifier fromOpaque(void *opaque) {
309-
return AvailabilityDomainOrIdentifier(Storage::getFromOpaqueValue(opaque));
319+
bool isDomain() const {
320+
return storage.getPointer().is<AvailabilityDomain>();
310321
}
311-
312-
bool isDomain() const { return storage.is<AvailabilityDomain>(); }
313-
bool isIdentifier() const { return storage.is<Identifier>(); }
322+
bool isIdentifier() const { return storage.getPointer().is<Identifier>(); }
314323

315324
/// Overwrites the existing domain or identifier with the given domain.
316325
void setDomain(AvailabilityDomain domain) { storage = Storage(domain); }
317326

318327
/// Returns the resolved domain, or `std::nullopt` if there isn't one.
319328
std::optional<AvailabilityDomain> getAsDomain() const {
320329
if (isDomain())
321-
return storage.get<AvailabilityDomain>();
330+
return storage.getPointer().get<AvailabilityDomain>();
322331
return std::nullopt;
323332
}
324333

325334
/// Returns the unresolved identifier, or `std::nullopt` if the domain has
326335
/// been resolved.
327336
std::optional<Identifier> getAsIdentifier() const {
328337
if (isIdentifier())
329-
return storage.get<Identifier>();
338+
return storage.getPointer().get<Identifier>();
330339
return std::nullopt;
331340
}
332341

342+
std::optional<AvailabilityDomain>
343+
resolveInDeclContext(SourceLoc loc, const DeclContext *declContext) {
344+
// Return the domain directly if already resolved.
345+
if (storage.getInt() || isDomain())
346+
return getAsDomain();
347+
348+
// Look up the domain and cache the result.
349+
auto result = lookUpInDeclContext(loc, declContext);
350+
if (result)
351+
storage.setPointer(*result);
352+
storage.setInt(true);
353+
354+
return result;
355+
}
356+
333357
/// Creates a new `AvailabilityDomainOrIdentifier`, defensively copying
334358
/// members of the original into the given `ASTContext` in case it is
335359
/// different than the context that the original was created for.

include/swift/AST/AvailabilitySpec.h

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ class SemanticAvailabilitySpec;
3535
/// The root class for specifications of API availability in availability
3636
/// queries.
3737
class AvailabilitySpec : public ASTAllocated<AvailabilitySpec> {
38-
using DomainStorage = llvm::PointerIntPair<AvailabilityDomainOrIdentifier, 1>;
39-
DomainStorage Storage;
38+
AvailabilityDomainOrIdentifier DomainOrIdentifier;
4039

4140
/// The range of the entire spec, including the version if there is one.
4241
SourceRange SrcRange;
@@ -51,11 +50,11 @@ class AvailabilitySpec : public ASTAllocated<AvailabilitySpec> {
5150
// Location of the availability macro expanded to create this spec.
5251
SourceLoc MacroLoc;
5352

54-
AvailabilitySpec(DomainStorage Storage, SourceRange SrcRange,
55-
llvm::VersionTuple Version, SourceLoc VersionStartLoc)
56-
: Storage(Storage), SrcRange(SrcRange), Version(Version),
57-
VersionStartLoc(VersionStartLoc) {}
58-
53+
AvailabilitySpec(AvailabilityDomainOrIdentifier DomainOrIdentifier,
54+
SourceRange SrcRange, llvm::VersionTuple Version,
55+
SourceLoc VersionStartLoc)
56+
: DomainOrIdentifier(DomainOrIdentifier), SrcRange(SrcRange),
57+
Version(Version), VersionStartLoc(VersionStartLoc) {}
5958

6059
public:
6160
/// Creates a wildcard availability specification that guards execution
@@ -99,7 +98,7 @@ class AvailabilitySpec : public ASTAllocated<AvailabilitySpec> {
9998
}
10099

101100
AvailabilityDomainOrIdentifier getDomainOrIdentifier() const {
102-
return Storage.getPointer();
101+
return DomainOrIdentifier;
103102
}
104103

105104
std::optional<AvailabilityDomain> getDomain() const {

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6748,9 +6748,8 @@ WARNING(attr_availability_expected_version_spec, none,
67486748
"expected 'introduced', 'deprecated', or 'obsoleted' in '%0' attribute "
67496749
"for platform '%1'", (StringRef, StringRef))
67506750
ERROR(attr_availability_requires_custom_availability, none,
6751-
"specifying '%0' in '%1' attribute requires "
6752-
"-enable-experimental-feature CustomAvailability",
6753-
(StringRef, const DeclAttribute))
6751+
"%0 requires -enable-experimental-feature CustomAvailability",
6752+
(Identifier))
67546753
WARNING(attr_availability_unexpected_version,none,
67556754
"unexpected version number in '%0' attribute for '%1'",
67566755
(const DeclAttribute, StringRef))

lib/AST/Availability.cpp

Lines changed: 6 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -797,69 +797,25 @@ bool AvailabilityInference::isAvailableAsSPI(const Decl *D) {
797797
return false;
798798
}
799799

800-
static std::optional<AvailabilityDomain>
801-
getAvailabilityDomainForName(Identifier identifier,
802-
const DeclContext *declContext) {
803-
if (auto builtinDomain = AvailabilityDomain::builtinDomainForString(
804-
identifier.str(), declContext))
805-
return builtinDomain;
806-
807-
auto &ctx = declContext->getASTContext();
808-
if (auto customDomain =
809-
ctx.MainModule->getAvailabilityDomainForIdentifier(identifier))
810-
return customDomain;
811-
812-
return std::nullopt;
813-
}
814-
815800
std::optional<SemanticAvailableAttr>
816801
SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator,
817802
const AvailableAttr *attr,
818803
const Decl *decl) const {
819-
if (attr->hasCachedDomain())
804+
if (attr->getDomainOrIdentifier().isDomain())
820805
return SemanticAvailableAttr(attr);
821806

822807
auto &ctx = decl->getASTContext();
823808
auto &diags = ctx.Diags;
824809
auto attrLoc = attr->getLocation();
825810
auto attrName = attr->getAttrName();
826811
auto domainLoc = attr->getDomainLoc();
812+
auto declContext = decl->getInnermostDeclContext();
827813
auto mutableAttr = const_cast<AvailableAttr *>(attr);
828-
auto domain = attr->getCachedDomain();
829-
830-
if (!domain) {
831-
auto domainIdentifier = attr->getDomainOrIdentifier().getAsIdentifier();
832-
ASSERT(domainIdentifier);
833-
834-
// Attempt to resolve the domain specified for the attribute and diagnose
835-
// if no domain is found.
836-
auto declContext = decl->getInnermostDeclContext();
837-
domain = getAvailabilityDomainForName(*domainIdentifier, declContext);
838-
if (!domain) {
839-
auto domainString = domainIdentifier->str();
840-
if (auto suggestion = closestCorrectedPlatformString(domainString)) {
841-
diags
842-
.diagnose(domainLoc, diag::attr_availability_suggest_platform,
843-
domainString, attrName, *suggestion)
844-
.fixItReplace(SourceRange(domainLoc), *suggestion);
845-
} else {
846-
diags.diagnose(attrLoc, diag::attr_availability_unknown_platform,
847-
domainString, attrName);
848-
}
849-
return std::nullopt;
850-
}
851-
852-
if (domain->isCustom() &&
853-
!ctx.LangOpts.hasFeature(Feature::CustomAvailability) &&
854-
!declContext->isInSwiftinterface()) {
855-
diags.diagnose(domainLoc,
856-
diag::attr_availability_requires_custom_availability,
857-
domain->getNameForAttributePrinting(), attr);
858-
return std::nullopt;
859-
}
814+
auto domain = mutableAttr->DomainOrIdentifier.resolveInDeclContext(
815+
domainLoc, declContext);
860816

861-
mutableAttr->setCachedDomain(*domain);
862-
}
817+
if (!domain)
818+
return std::nullopt;
863819

864820
auto domainName = domain->getNameForAttributePrinting();
865821
auto semanticAttr = SemanticAvailableAttr(attr);

lib/AST/AvailabilityDomain.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
#include "swift/AST/AvailabilityDomain.h"
1414
#include "swift/AST/ASTContext.h"
1515
#include "swift/AST/Decl.h"
16+
#include "swift/AST/DiagnosticsParse.h"
17+
#include "swift/AST/DiagnosticsSema.h"
18+
#include "swift/AST/TypeCheckRequests.h"
1619
#include "swift/Basic/Assertions.h"
1720
#include "llvm/ADT/StringSwitch.h"
1821

@@ -201,6 +204,57 @@ CustomAvailabilityDomain::create(const ASTContext &ctx, StringRef name,
201204
return new (ctx) CustomAvailabilityDomain(ctx.getIdentifier(name), mod, kind);
202205
}
203206

207+
static std::optional<AvailabilityDomain>
208+
getAvailabilityDomainForName(Identifier identifier,
209+
const DeclContext *declContext) {
210+
if (auto builtinDomain = AvailabilityDomain::builtinDomainForString(
211+
identifier.str(), declContext))
212+
return builtinDomain;
213+
214+
auto &ctx = declContext->getASTContext();
215+
if (auto customDomain =
216+
ctx.MainModule->getAvailabilityDomainForIdentifier(identifier))
217+
return customDomain;
218+
219+
return std::nullopt;
220+
}
221+
222+
std::optional<AvailabilityDomain>
223+
AvailabilityDomainOrIdentifier::lookUpInDeclContext(
224+
SourceLoc loc, const DeclContext *declContext) const {
225+
DEBUG_ASSERT(isIdentifier());
226+
227+
auto &ctx = declContext->getASTContext();
228+
auto &diags = ctx.Diags;
229+
std::optional<AvailabilityDomain> domain;
230+
auto identifier = getAsIdentifier().value();
231+
domain = getAvailabilityDomainForName(identifier, declContext);
232+
233+
if (!domain) {
234+
auto domainString = identifier.str();
235+
if (auto suggestion = closestCorrectedPlatformString(domainString)) {
236+
diags
237+
.diagnose(loc, diag::avail_query_suggest_platform_name, identifier,
238+
*suggestion)
239+
.fixItReplace(SourceRange(loc), *suggestion);
240+
} else {
241+
diags.diagnose(loc, diag::avail_query_unrecognized_platform_name,
242+
identifier);
243+
}
244+
return std::nullopt;
245+
}
246+
247+
if (domain->isCustom() &&
248+
!ctx.LangOpts.hasFeature(Feature::CustomAvailability) &&
249+
!declContext->isInSwiftinterface()) {
250+
diags.diagnose(loc, diag::attr_availability_requires_custom_availability,
251+
identifier);
252+
return std::nullopt;
253+
}
254+
255+
return domain;
256+
}
257+
204258
AvailabilityDomainOrIdentifier
205259
AvailabilityDomainOrIdentifier::copy(ASTContext &ctx) const {
206260
if (auto identifier = getAsIdentifier())

lib/AST/AvailabilitySpec.cpp

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ using namespace swift;
2323

2424
AvailabilitySpec *AvailabilitySpec::createWildcard(ASTContext &ctx,
2525
SourceLoc starLoc) {
26-
return new (ctx)
27-
AvailabilitySpec({AvailabilityDomain::forUniversal(), true}, starLoc,
28-
/*Version=*/{},
29-
/*VersionStartLoc=*/{});
26+
return new (ctx) AvailabilitySpec(AvailabilityDomain::forUniversal(), starLoc,
27+
/*Version=*/{},
28+
/*VersionStartLoc=*/{});
3029
}
3130

3231
AvailabilitySpec *AvailabilitySpec::createForDomain(ASTContext &ctx,
@@ -35,23 +34,22 @@ AvailabilitySpec *AvailabilitySpec::createForDomain(ASTContext &ctx,
3534
llvm::VersionTuple version,
3635
SourceRange versionRange) {
3736
DEBUG_ASSERT(!version.empty());
38-
return new (ctx)
39-
AvailabilitySpec({domain, true}, SourceRange(loc, versionRange.End),
40-
version, versionRange.Start);
37+
return new (ctx) AvailabilitySpec(domain, SourceRange(loc, versionRange.End),
38+
version, versionRange.Start);
4139
}
4240

4341
AvailabilitySpec *AvailabilitySpec::createForUnknownDomain(
4442
ASTContext &ctx, Identifier domainIdentifier, SourceLoc loc,
4543
llvm::VersionTuple version, SourceRange versionRange) {
46-
return new (ctx) AvailabilitySpec({domainIdentifier, true},
47-
SourceRange(loc, versionRange.End), version,
48-
versionRange.Start);
44+
DEBUG_ASSERT(!version.empty());
45+
return new (ctx)
46+
AvailabilitySpec(domainIdentifier, SourceRange(loc, versionRange.End),
47+
version, versionRange.Start);
4948
}
5049

5150
AvailabilitySpec *AvailabilitySpec::clone(ASTContext &ctx) const {
52-
return new (ctx)
53-
AvailabilitySpec({getDomainOrIdentifier().copy(ctx), true},
54-
SrcRange, Version, VersionStartLoc);
51+
return new (ctx) AvailabilitySpec(getDomainOrIdentifier().copy(ctx), SrcRange,
52+
Version, VersionStartLoc);
5553
}
5654

5755
void AvailabilitySpec::print(llvm::raw_ostream &os) const {

lib/AST/TypeCheckRequests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2725,7 +2725,7 @@ SemanticAvailableAttrRequest::getCachedResult() const {
27252725
if (!attr->hasComputedSemanticAttr())
27262726
return std::nullopt;
27272727

2728-
if (!attr->hasCachedDomain()) {
2728+
if (!attr->getDomainOrIdentifier().isDomain()) {
27292729
return std::optional<SemanticAvailableAttr>{};
27302730
}
27312731

0 commit comments

Comments
 (0)