Skip to content

Commit afd9972

Browse files
authored
Merge pull request #79070 from tshortli/model-custom-availability-domains
AST/Sema: Model custom availability domains
2 parents cbbfd17 + 79c0173 commit afd9972

13 files changed

+302
-66
lines changed

include/swift/AST/Attr.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3319,7 +3319,7 @@ class SemanticAvailableAttr final {
33193319
return attr->getRawIntroduced();
33203320
}
33213321

3322-
/// The source range of the `introduced:` component.
3322+
/// The source range of the `introduced:` version component.
33233323
SourceRange getIntroducedSourceRange() const { return attr->IntroducedRange; }
33243324

33253325
/// Returns the effective range in which the declaration with this attribute
@@ -3331,11 +3331,17 @@ class SemanticAvailableAttr final {
33313331
return attr->getRawDeprecated();
33323332
}
33333333

3334+
/// The source range of the `deprecated:` version component.
3335+
SourceRange getDeprecatedSourceRange() const { return attr->DeprecatedRange; }
3336+
33343337
/// The version tuple written in source for the `obsoleted:` component.
33353338
std::optional<llvm::VersionTuple> getObsoleted() const {
33363339
return attr->getRawObsoleted();
33373340
}
33383341

3342+
/// The source range of the `obsoleted:` version component.
3343+
SourceRange getObsoletedSourceRange() const { return attr->ObsoletedRange; }
3344+
33393345
/// Returns the `message:` field of the attribute, or an empty string.
33403346
StringRef getMessage() const { return attr->Message; }
33413347

include/swift/AST/AvailabilityDomain.h

Lines changed: 63 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#ifndef SWIFT_AST_AVAILABILITY_DOMAIN_H
1919
#define SWIFT_AST_AVAILABILITY_DOMAIN_H
2020

21+
#include "swift/AST/ASTAllocated.h"
22+
#include "swift/AST/Identifier.h"
2123
#include "swift/AST/PlatformKind.h"
2224
#include "swift/Basic/LLVM.h"
2325
#include "llvm/ADT/FoldingSet.h"
@@ -26,7 +28,9 @@
2628

2729
namespace swift {
2830
class ASTContext;
31+
class CustomAvailabilityDomain;
2932
class DeclContext;
33+
class ModuleDecl;
3034

3135
/// Represents a dimension of availability (e.g. macOS platform or Swift
3236
/// language mode).
@@ -48,6 +52,10 @@ class AvailabilityDomain final {
4852

4953
/// Represents availability for a specific operating system platform.
5054
Platform,
55+
56+
/// Represents availability for an arbitrary domain that is defined at
57+
/// compile time by a module.
58+
Custom,
5159
};
5260

5361
private:
@@ -86,13 +94,10 @@ class AvailabilityDomain final {
8694
PlatformKind getPlatform() { return platform; }
8795
};
8896

89-
/// This will eventually be a class storing information about externally
90-
/// defined availability domains.
91-
using ExternalDomain = void;
92-
9397
using InlineDomainPtr =
9498
llvm::PointerEmbeddedInt<uint32_t, InlineDomain::ReprBits>;
95-
using Storage = llvm::PointerUnion<ExternalDomain *, InlineDomainPtr>;
99+
using Storage =
100+
llvm::PointerUnion<CustomAvailabilityDomain *, InlineDomainPtr>;
96101
Storage storage;
97102

98103
AvailabilityDomain(Kind kind)
@@ -103,6 +108,8 @@ class AvailabilityDomain final {
103108
AvailabilityDomain(PlatformKind platform)
104109
: storage(InlineDomain(Kind::Platform, platform).asInteger()) {};
105110

111+
AvailabilityDomain(CustomAvailabilityDomain *domain) : storage(domain) {};
112+
106113
AvailabilityDomain(Storage storage) : storage(storage) {};
107114

108115
static AvailabilityDomain fromOpaque(void *opaque) {
@@ -118,6 +125,11 @@ class AvailabilityDomain final {
118125
: std::nullopt;
119126
}
120127

128+
CustomAvailabilityDomain *getCustomDomain() const {
129+
assert(isCustom());
130+
return storage.get<CustomAvailabilityDomain *>();
131+
}
132+
121133
public:
122134
AvailabilityDomain() {}
123135

@@ -141,6 +153,10 @@ class AvailabilityDomain final {
141153
return AvailabilityDomain(Kind::Embedded);
142154
}
143155

156+
static AvailabilityDomain forCustom(CustomAvailabilityDomain *domain) {
157+
return AvailabilityDomain(domain);
158+
}
159+
144160
/// Returns the built-in availability domain identified by the given string.
145161
static std::optional<AvailabilityDomain>
146162
builtinDomainForString(StringRef string, const DeclContext *declContext);
@@ -149,7 +165,7 @@ class AvailabilityDomain final {
149165
if (auto inlineDomain = getInlineDomain())
150166
return inlineDomain->getKind();
151167

152-
llvm_unreachable("unimplemented");
168+
return Kind::Custom;
153169
}
154170

155171
bool isUniversal() const { return getKind() == Kind::Universal; }
@@ -164,6 +180,8 @@ class AvailabilityDomain final {
164180

165181
bool isEmbedded() const { return getKind() == Kind::Embedded; }
166182

183+
bool isCustom() const { return getKind() == Kind::Custom; }
184+
167185
/// Returns the platform kind for this domain if applicable.
168186
PlatformKind getPlatformKind() const {
169187
if (auto inlineDomain = getInlineDomain())
@@ -172,6 +190,10 @@ class AvailabilityDomain final {
172190
return PlatformKind::none;
173191
}
174192

193+
/// Returns true if availability for this domain can be specified in terms of
194+
/// version ranges.
195+
bool isVersioned() const;
196+
175197
/// Returns true if this domain is considered active in the current
176198
/// compilation context.
177199
bool isActive(const ASTContext &ctx) const;
@@ -183,6 +205,9 @@ class AvailabilityDomain final {
183205
/// Returns the string to use when printing an `@available` attribute.
184206
llvm::StringRef getNameForAttributePrinting() const;
185207

208+
/// Returns the module that the domain belongs to, if it is a custom domain.
209+
ModuleDecl *getModule() const;
210+
186211
/// Returns true if availability in `other` is a subset of availability in
187212
/// this domain. The set of all availability domains form a lattice where the
188213
/// universal domain (`*`) is the bottom element.
@@ -196,28 +221,39 @@ class AvailabilityDomain final {
196221
return !(*this == other);
197222
}
198223

199-
/// A total, stable ordering on domains.
200-
bool operator<(const AvailabilityDomain &other) const {
201-
if (getKind() != other.getKind())
202-
return getKind() < other.getKind();
203-
204-
switch (getKind()) {
205-
case Kind::Universal:
206-
case Kind::SwiftLanguage:
207-
case Kind::PackageDescription:
208-
case Kind::Embedded:
209-
// These availability domains are singletons.
210-
return false;
211-
case Kind::Platform:
212-
return getPlatformKind() < other.getPlatformKind();
213-
}
214-
}
215-
216224
void Profile(llvm::FoldingSetNodeID &ID) const {
217225
ID.AddPointer(getOpaqueValue());
218226
}
219227
};
220228

229+
/// Represents an availability domain that has been defined in a module.
230+
class CustomAvailabilityDomain : public ASTAllocated<CustomAvailabilityDomain> {
231+
public:
232+
enum class Kind {
233+
/// A domain that is known to be enabled at compile time.
234+
Enabled,
235+
/// A domain that is known to be disabled at compile time.
236+
Disabled,
237+
/// A domain with an enablement state that must be queried at runtime.
238+
Dynamic,
239+
};
240+
241+
private:
242+
Identifier name;
243+
Kind kind;
244+
ModuleDecl *mod;
245+
246+
CustomAvailabilityDomain(Identifier name, ModuleDecl *mod, Kind kind);
247+
248+
public:
249+
static CustomAvailabilityDomain *create(const ASTContext &ctx, StringRef name,
250+
ModuleDecl *mod, Kind kind);
251+
252+
Identifier getName() const { return name; }
253+
Kind getKind() const { return kind; }
254+
ModuleDecl *getModule() const { return mod; }
255+
};
256+
221257
} // end namespace swift
222258

223259
namespace llvm {
@@ -249,6 +285,10 @@ struct DenseMapInfo<AvailabilityDomain> {
249285
static inline AvailabilityDomain getTombstoneKey() {
250286
return DenseMapInfo<AvailabilityDomain::Storage>::getTombstoneKey();
251287
}
288+
static inline unsigned getHashValue(AvailabilityDomain domain) {
289+
return DenseMapInfo<AvailabilityDomain::Storage>::getHashValue(
290+
domain.storage);
291+
}
252292
static bool isEqual(const AvailabilityDomain LHS,
253293
const AvailabilityDomain RHS) {
254294
return LHS == RHS;

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6731,6 +6731,13 @@ WARNING(attr_availability_cannot_be_used_for_domain, none,
67316731
WARNING(attr_availability_expected_version_spec, none,
67326732
"expected 'introduced', 'deprecated', or 'obsoleted' in '%0' attribute "
67336733
"for platform '%1'", (StringRef, StringRef))
6734+
ERROR(attr_availability_requires_custom_availability, none,
6735+
"specifying '%0' in '%1' attribute requires "
6736+
"-enable-experimental-feature CustomAvailability",
6737+
(StringRef, const DeclAttribute))
6738+
WARNING(attr_availability_unexpected_version,none,
6739+
"unexpected version number in '%0' attribute for '%1'",
6740+
(const DeclAttribute, StringRef))
67346741

67356742
ERROR(availability_decl_unavailable, none,
67366743
"%0 is unavailable%select{ in %2|}1%select{|: %3}3",

include/swift/AST/Module.h

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "swift/AST/AccessNotes.h"
2121
#include "swift/AST/AttrKind.h"
22+
#include "swift/AST/AvailabilityDomain.h"
2223
#include "swift/AST/Decl.h"
2324
#include "swift/AST/DeclContext.h"
2425
#include "swift/AST/Identifier.h"
@@ -41,8 +42,8 @@
4142
#include "llvm/Support/ErrorHandling.h"
4243
#include "llvm/Support/MD5.h"
4344
#include <optional>
44-
#include <unordered_map>
4545
#include <set>
46+
#include <unordered_map>
4647

4748
namespace clang {
4849
class Module;
@@ -51,20 +52,16 @@ namespace clang {
5152
namespace swift {
5253
enum class ArtificialMainKind : uint8_t;
5354
class ASTContext;
54-
class ASTScope;
5555
class ASTWalker;
56-
class AvailabilityScope;
57-
class BraceStmt;
56+
class CustomAvailabilityDomain;
5857
class Decl;
5958
class DeclAttribute;
6059
class TypeDecl;
6160
enum class DeclKind : uint8_t;
62-
class ExtensionDecl;
6361
class DebuggerClient;
6462
class DeclName;
6563
class FileUnit;
6664
class FuncDecl;
67-
class InfixOperatorDecl;
6865
enum class LibraryLevel : uint8_t;
6966
class LinkLibrary;
7067
class ModuleLoader;
@@ -74,20 +71,12 @@ namespace swift {
7471
class PostfixOperatorDecl;
7572
class PrefixOperatorDecl;
7673
class ProtocolConformance;
77-
class ProtocolDecl;
7874
struct PrintOptions;
7975
class SourceLookupCache;
80-
class Token;
81-
class TupleType;
8276
class Type;
8377
class ValueDecl;
84-
class VarDecl;
8578
class VisibleDeclConsumer;
8679

87-
namespace ast_scope {
88-
class ASTSourceFileScope;
89-
}
90-
9180
/// Discriminator for file-units.
9281
enum class FileUnitKind {
9382
/// For a .swift source file.
@@ -361,6 +350,10 @@ class ModuleDecl
361350
/// Used by the debugger to bypass resilient access to fields.
362351
bool BypassResilience = false;
363352

353+
using AvailabilityDomainMap =
354+
llvm::SmallDenseMap<Identifier, CustomAvailabilityDomain *>;
355+
AvailabilityDomainMap AvailabilityDomains;
356+
364357
public:
365358
using PopulateFilesFn = llvm::function_ref<void(
366359
ModuleDecl *, llvm::function_ref<void(FileUnit *)>)>;
@@ -1233,6 +1226,15 @@ class ModuleDecl
12331226
/// An empty `Version` is returned if the information is not available.
12341227
version::Version getLanguageVersionBuiltWith() const;
12351228

1229+
/// Returns the custom availability domain defined by this module with the
1230+
/// given identifier, if one exists.
1231+
std::optional<AvailabilityDomain>
1232+
getAvailabilityDomainForIdentifier(Identifier identifier) const;
1233+
1234+
void setAvailabilityDomains(const AvailabilityDomainMap &&map) {
1235+
AvailabilityDomains = std::move(map);
1236+
}
1237+
12361238
static bool classof(const DeclContext *DC) {
12371239
if (auto D = DC->getAsDecl())
12381240
return classof(D);

include/swift/AST/SourceFile.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@
2525
#include "llvm/ADT/STLExtras.h"
2626

2727
namespace swift {
28-
28+
class ASTScope;
29+
class AvailabilityScope;
2930
class PersistentParserState;
3031
struct SourceFileExtras;
32+
class Token;
3133

3234
/// Kind of import affecting how a decl can be reexported.
3335
///

lib/AST/Availability.cpp

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,46 @@ void AvailabilityInference::applyInferredAvailableAttrs(
201201
Decl *ToDecl, ArrayRef<const Decl *> InferredFromDecls) {
202202
auto &Context = ToDecl->getASTContext();
203203

204+
/// A wrapper for AvailabilityDomain that implements a stable, total ordering for
205+
/// domains. This is needed to ensure that the inferred attributes are added to
206+
/// the declaration in a consistent order, preserving interface printing output
207+
/// stability across compilations.
208+
class OrderedAvailabilityDomain {
209+
public:
210+
AvailabilityDomain domain;
211+
212+
OrderedAvailabilityDomain(AvailabilityDomain domain) : domain(domain) {}
213+
214+
bool operator<(const OrderedAvailabilityDomain &other) const {
215+
auto kind = domain.getKind();
216+
auto otherKind = other.domain.getKind();
217+
if (kind != otherKind)
218+
return kind < otherKind;
219+
220+
switch (kind) {
221+
case AvailabilityDomain::Kind::Universal:
222+
case AvailabilityDomain::Kind::SwiftLanguage:
223+
case AvailabilityDomain::Kind::PackageDescription:
224+
case AvailabilityDomain::Kind::Embedded:
225+
return false;
226+
case AvailabilityDomain::Kind::Platform:
227+
return domain.getPlatformKind() < other.domain.getPlatformKind();
228+
case AvailabilityDomain::Kind::Custom: {
229+
auto mod = domain.getModule();
230+
auto otherMod = other.domain.getModule();
231+
if (mod != otherMod)
232+
return mod->getName() < otherMod->getName();
233+
234+
return domain.getNameForAttributePrinting() <
235+
other.domain.getNameForAttributePrinting();
236+
}
237+
}
238+
}
239+
};
240+
204241
// Iterate over the declarations and infer required availability on
205242
// a per-platform basis.
206-
std::map<AvailabilityDomain, InferredAvailability> Inferred;
243+
std::map<OrderedAvailabilityDomain, InferredAvailability> Inferred;
207244
for (const Decl *D : InferredFromDecls) {
208245
llvm::SmallVector<SemanticAvailableAttr, 8> MergedAttrs;
209246

@@ -238,7 +275,8 @@ void AvailabilityInference::applyInferredAvailableAttrs(
238275
// Create an availability attribute for each observed platform and add
239276
// to ToDecl.
240277
for (auto &Pair : Inferred) {
241-
if (auto Attr = createAvailableAttr(Pair.first, Pair.second, Context))
278+
if (auto Attr =
279+
createAvailableAttr(Pair.first.domain, Pair.second, Context))
242280
Attrs.add(Attr);
243281
}
244282
}

0 commit comments

Comments
 (0)