Skip to content

Commit ece70eb

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 ece70eb

11 files changed

+122
-96
lines changed

include/swift/AST/Attr.h

Lines changed: 1 addition & 5 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;
@@ -918,11 +919,6 @@ class AvailableAttr : public DeclAttribute {
918919
private:
919920
friend class SemanticAvailableAttrRequest;
920921

921-
void setCachedDomain(AvailabilityDomain domain) {
922-
assert(!DomainOrIdentifier.isDomain());
923-
DomainOrIdentifier.setDomain(domain);
924-
}
925-
926922
bool hasComputedSemanticAttr() const {
927923
return Bits.AvailableAttr.HasComputedSemanticAttr;
928924
}

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())
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: 5 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -797,21 +797,6 @@ 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,
@@ -824,42 +809,13 @@ SemanticAvailableAttrRequest::evaluate(swift::Evaluator &evaluator,
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 {

test/attr/attr_availability.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,26 @@ func noArgs() {}
1717
@available(*) // expected-error {{expected ',' in 'available' attribute}}
1818
func noKind() {}
1919

20-
@available(badPlatform, unavailable) // expected-warning {{unknown platform 'badPlatform' for attribute 'available'}}
20+
@available(badPlatform, unavailable) // expected-warning {{unrecognized platform name 'badPlatform'}}
2121
func unavailable_bad_platform() {}
2222

23-
@available(macos, unavailable) // expected-warning {{unknown platform 'macos' for attribute 'available'; did you mean 'macOS'?}} {{12-17=macOS}}
23+
@available(macos, unavailable) // expected-warning {{unrecognized platform name 'macos'; did you mean 'macOS'?}} {{12-17=macOS}}
2424
func incorrect_platform_case() {}
2525

26-
@available(mscos, unavailable) // expected-warning {{unknown platform 'mscos' for attribute 'available'; did you mean 'macOS'?}} {{12-17=macOS}}
26+
@available(mscos, unavailable) // expected-warning {{unrecognized platform name 'mscos'; did you mean 'macOS'?}} {{12-17=macOS}}
2727
func incorrect_platform_similar1() {}
2828

29-
@available(macoss, unavailable) // expected-warning {{unknown platform 'macoss' for attribute 'available'; did you mean 'macOS'?}} {{12-18=macOS}}
29+
@available(macoss, unavailable) // expected-warning {{unrecognized platform name 'macoss'; did you mean 'macOS'?}} {{12-18=macOS}}
3030
func incorrect_platform_similar2() {}
3131

32-
@available(mac, unavailable) // expected-warning {{unknown platform 'mac' for attribute 'available'; did you mean 'macOS'?}} {{12-15=macOS}}
32+
@available(mac, unavailable) // expected-warning {{unrecognized platform name 'mac'; did you mean 'macOS'?}} {{12-15=macOS}}
3333
func incorrect_platform_similar3() {}
3434

35-
@available(notValid, unavailable) // expected-warning {{unknown platform 'notValid' for attribute 'available'}} {{none}}
35+
@available(notValid, unavailable) // expected-warning {{unrecognized platform name 'notValid'}} {{none}}
3636
func incorrect_platform_not_similar() {}
3737

3838
// Handle unknown platform.
39-
@available(HAL9000, unavailable) // expected-warning {{unknown platform 'HAL9000'}}
39+
@available(HAL9000, unavailable) // expected-warning {{unrecognized platform name 'HAL9000'}}
4040
func availabilityUnknownPlatform() {}
4141

4242
// <rdar://problem/17669805> Availability can't appear on a typealias

test/attr/attr_availability_custom_domains.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func deprecatedInRedefinedDomain() { }
2929
@available(DynamicDomain)
3030
func availableInDynamicDomain() { }
3131

32-
@available(UnknownDomain) // expected-warning {{unknown platform 'UnknownDomain' for attribute 'available'}}
32+
@available(UnknownDomain) // expected-warning {{unrecognized platform name 'UnknownDomain'}}
3333
func availableInUnknownDomain() { }
3434

3535
func test() {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// RUN: %target-typecheck-verify-swift \
22
// RUN: -define-enabled-availability-domain SomeDomain
33

4-
@available(SomeDomain, unavailable) // expected-error {{specifying 'SomeDomain' in '@available' attribute requires -enable-experimental-feature CustomAvailability}}
4+
@available(SomeDomain, unavailable) // expected-error {{'SomeDomain' requires -enable-experimental-feature CustomAvailability}}
55
func availableInSomeDomain() { }

test/decl/protocol/special/case_iterable/case_iterable_supported.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ extension FromOtherFile: CaseIterable {
4040

4141
enum InvalidAvailableAttribute: CaseIterable {
4242
case a
43-
@available(deprecated, renamed: "a") // expected-warning {{unknown platform 'deprecated' for attribute 'available'}}
43+
@available(deprecated, renamed: "a") // expected-warning {{unrecognized platform name 'deprecated'}}
4444
case b
4545
}

0 commit comments

Comments
 (0)