Skip to content

Commit 0824210

Browse files
committed
[AST/Sema] Add availability attributes for macCatalyst
Add a platform kind and availability attributes for macCatalyst. macCatalyst uses iOS version numbers and inherits availability from iOS attributes unless a macCatalyst attribute is explicitly provided.
1 parent 8c5c5ec commit 0824210

File tree

15 files changed

+345
-11
lines changed

15 files changed

+345
-11
lines changed

include/swift/AST/Attr.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1997,6 +1997,11 @@ class DeclAttributes {
19971997
bool
19981998
isUnavailableInSwiftVersion(const version::Version &effectiveVersion) const;
19991999

2000+
/// Finds the most-specific platform-specific attribute that is
2001+
/// active for the current platform.
2002+
const AvailableAttr *
2003+
findMostSpecificActivePlatform(const ASTContext &ctx) const;
2004+
20002005
/// Returns the first @available attribute that indicates
20012006
/// a declaration is unavailable, or the first one that indicates it's
20022007
/// potentially unavailable, or null otherwise.

include/swift/AST/PlatformKind.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,20 @@ StringRef prettyPlatformString(PlatformKind platform);
5151
/// restrictions are enabled, but OSXApplicationExtension is not considered
5252
/// active when the target platform is OS X and app extension restrictions are
5353
/// disabled. PlatformKind::none is always considered active.
54-
bool isPlatformActive(PlatformKind Platform, LangOptions &LangOpts);
55-
54+
/// If ForTargetVariant is true then for zippered builds the target-variant
55+
/// triple will be used rather than the target to determine whether the
56+
/// platform is active.
57+
bool isPlatformActive(PlatformKind Platform, LangOptions &LangOpts,
58+
bool ForTargetVariant = false);
59+
5660
/// Returns the target platform for the given language options.
5761
PlatformKind targetPlatform(LangOptions &LangOpts);
58-
62+
63+
/// Returns true when availability attributes from the "parent" platform
64+
/// should also apply to the "child" platform for declarations without
65+
/// an explicit attribute for the child.
66+
bool inheritsAvailabilityFromPlatform(PlatformKind Child, PlatformKind Parent);
67+
5968
} // end namespace swift
6069

6170
#endif

include/swift/AST/PlatformKinds.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,7 @@ AVAILABILITY_PLATFORM(iOSApplicationExtension, "application extensions for iOS")
3030
AVAILABILITY_PLATFORM(tvOSApplicationExtension, "application extensions for tvOS")
3131
AVAILABILITY_PLATFORM(watchOSApplicationExtension, "application extensions for watchOS")
3232
AVAILABILITY_PLATFORM(OSXApplicationExtension, "application extensions for macOS")
33+
AVAILABILITY_PLATFORM(macCatalyst, "Mac Catalyst")
34+
AVAILABILITY_PLATFORM(macCatalystApplicationExtension, "application extensions for Mac Catalyst")
3335

3436
#undef AVAILABILITY_PLATFORM

include/swift/AST/Stmt.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,11 +329,20 @@ class alignas(8) PoundAvailableInfo final :
329329
/// The version range when this query will return true. This value is
330330
/// filled in by Sema.
331331
VersionRange AvailableRange;
332-
332+
333+
/// For zippered builds, this is the version range for the target variant
334+
/// that must hold for the query to return true. For example, when
335+
/// compiling with target x86_64-macosx10.15 and target-variant
336+
/// x86_64-ios13.0 a query of #available(macOS 10.22, iOS 20.0, *) will
337+
/// have a variant range of [20.0, +inf).
338+
/// This is filled in by Sema.
339+
VersionRange VariantAvailableRange;
340+
333341
PoundAvailableInfo(SourceLoc PoundLoc, ArrayRef<AvailabilitySpec *> queries,
334342
SourceLoc RParenLoc)
335343
: PoundLoc(PoundLoc), RParenLoc(RParenLoc), NumQueries(queries.size()),
336-
AvailableRange(VersionRange::empty()) {
344+
AvailableRange(VersionRange::empty()),
345+
VariantAvailableRange(VersionRange::empty()) {
337346
std::uninitialized_copy(queries.begin(), queries.end(),
338347
getTrailingObjects<AvailabilitySpec *>());
339348
}
@@ -356,6 +365,13 @@ class alignas(8) PoundAvailableInfo final :
356365

357366
const VersionRange &getAvailableRange() const { return AvailableRange; }
358367
void setAvailableRange(const VersionRange &Range) { AvailableRange = Range; }
368+
369+
const VersionRange &getVariantAvailableRange() const {
370+
return VariantAvailableRange;
371+
}
372+
void setVariantAvailableRange(const VersionRange &Range) {
373+
VariantAvailableRange = Range;
374+
}
359375
};
360376

361377

lib/AST/Attr.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,35 @@ DeclAttributes::isUnavailableInSwiftVersion(
172172
return false;
173173
}
174174

175+
const AvailableAttr *
176+
DeclAttributes::findMostSpecificActivePlatform(const ASTContext &ctx) const{
177+
const AvailableAttr *bestAttr = nullptr;
178+
179+
for (auto attr : *this) {
180+
auto *avAttr = dyn_cast<AvailableAttr>(attr);
181+
if (!avAttr)
182+
continue;
183+
184+
if (avAttr->isInvalid())
185+
continue;
186+
187+
if (!avAttr->hasPlatform())
188+
continue;
189+
190+
if (!avAttr->isActivePlatform(ctx))
191+
continue;
192+
193+
// We have an attribute that is active for the platform, but
194+
// is it more specific than our curent best?
195+
if (!bestAttr || inheritsAvailabilityFromPlatform(avAttr->Platform,
196+
bestAttr->Platform)) {
197+
bestAttr = avAttr;
198+
}
199+
}
200+
201+
return bestAttr;
202+
}
203+
175204
const AvailableAttr *
176205
DeclAttributes::getPotentiallyUnavailable(const ASTContext &ctx) const {
177206
const AvailableAttr *potential = nullptr;
@@ -217,12 +246,19 @@ DeclAttributes::getPotentiallyUnavailable(const ASTContext &ctx) const {
217246
const AvailableAttr *DeclAttributes::getUnavailable(
218247
const ASTContext &ctx) const {
219248
const AvailableAttr *conditional = nullptr;
249+
const AvailableAttr *bestActive = findMostSpecificActivePlatform(ctx);
220250

221251
for (auto Attr : *this)
222252
if (auto AvAttr = dyn_cast<AvailableAttr>(Attr)) {
223253
if (AvAttr->isInvalid())
224254
continue;
225255

256+
// If this is a platform-specific attribute and it isn't the most
257+
// specific attribute for the current platform, we're done.
258+
if (AvAttr->hasPlatform() &&
259+
(!bestActive || AvAttr != bestActive))
260+
continue;
261+
226262
// If this attribute doesn't apply to the active platform, we're done.
227263
if (!AvAttr->isActivePlatform(ctx) &&
228264
!AvAttr->isLanguageVersionSpecific() &&
@@ -250,11 +286,16 @@ const AvailableAttr *DeclAttributes::getUnavailable(
250286
const AvailableAttr *
251287
DeclAttributes::getDeprecated(const ASTContext &ctx) const {
252288
const AvailableAttr *conditional = nullptr;
289+
const AvailableAttr *bestActive = findMostSpecificActivePlatform(ctx);
253290
for (auto Attr : *this) {
254291
if (auto AvAttr = dyn_cast<AvailableAttr>(Attr)) {
255292
if (AvAttr->isInvalid())
256293
continue;
257294

295+
if (AvAttr->hasPlatform() &&
296+
(!bestActive || AvAttr != bestActive))
297+
continue;
298+
258299
if (!AvAttr->isActivePlatform(ctx) &&
259300
!AvAttr->isLanguageVersionSpecific() &&
260301
!AvAttr->isPackageDescriptionVersionSpecific())
@@ -329,6 +370,30 @@ static bool isShortAvailable(const DeclAttribute *DA) {
329370
return true;
330371
}
331372

373+
/// Return true when another availability attribute implies the same availability as this
374+
/// attribute and so printing the attribute can be skipped to de-clutter the declaration
375+
/// when printing the short form.
376+
/// For example, iOS availability implies macCatalyst availability so if attributes for
377+
/// both are present and they have the same 'introduced' version, we can skip printing an
378+
/// explicit availability for macCatalyst.
379+
static bool isShortFormAvailabilityImpliedByOther(const AvailableAttr *Attr,
380+
ArrayRef<const DeclAttribute *> Others) {
381+
assert(isShortAvailable(Attr));
382+
383+
for (auto *DA : Others) {
384+
auto *Other = cast<AvailableAttr>(DA);
385+
if (Attr->Platform == Other->Platform)
386+
continue;
387+
388+
if (!inheritsAvailabilityFromPlatform(Attr->Platform, Other->Platform))
389+
continue;
390+
391+
if (Attr->Introduced == Other->Introduced)
392+
return true;
393+
}
394+
return false;
395+
}
396+
332397
/// Print the short-form @available() attribute for an array of long-form
333398
/// AvailableAttrs that can be represented in the short form.
334399
/// For example, for:
@@ -359,6 +424,8 @@ static void printShortFormAvailable(ArrayRef<const DeclAttribute *> Attrs,
359424
for (auto *DA : Attrs) {
360425
auto *AvailAttr = cast<AvailableAttr>(DA);
361426
assert(AvailAttr->Introduced.hasValue());
427+
if (isShortFormAvailabilityImpliedByOther(AvailAttr, Attrs))
428+
continue;
362429
Printer << platformString(AvailAttr->Platform) << " "
363430
<< AvailAttr->Introduced.getValue().getAsString() << ", ";
364431
}

lib/AST/PlatformKind.cpp

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

1717
#include "swift/AST/PlatformKind.h"
1818
#include "swift/Basic/LangOptions.h"
19+
#include "swift/Basic/Platform.h"
1920
#include "llvm/ADT/StringSwitch.h"
2021
#include "llvm/Support/ErrorHandling.h"
2122

@@ -63,7 +64,8 @@ static bool isPlatformActiveForTarget(PlatformKind Platform,
6364
return true;
6465

6566
if (Platform == PlatformKind::OSXApplicationExtension ||
66-
Platform == PlatformKind::iOSApplicationExtension)
67+
Platform == PlatformKind::iOSApplicationExtension ||
68+
Platform == PlatformKind::macCatalystApplicationExtension)
6769
if (!EnableAppExtensionRestrictions)
6870
return false;
6971

@@ -75,6 +77,9 @@ static bool isPlatformActiveForTarget(PlatformKind Platform,
7577
case PlatformKind::iOS:
7678
case PlatformKind::iOSApplicationExtension:
7779
return Target.isiOS() && !Target.isTvOS();
80+
case PlatformKind::macCatalyst:
81+
case PlatformKind::macCatalystApplicationExtension:
82+
return tripleIsMacCatalystEnvironment(Target);
7883
case PlatformKind::tvOS:
7984
case PlatformKind::tvOSApplicationExtension:
8085
return Target.isTvOS();
@@ -87,8 +92,15 @@ static bool isPlatformActiveForTarget(PlatformKind Platform,
8792
llvm_unreachable("bad PlatformKind");
8893
}
8994

90-
bool swift::isPlatformActive(PlatformKind Platform, LangOptions &LangOpts) {
95+
bool swift::isPlatformActive(PlatformKind Platform, LangOptions &LangOpts,
96+
bool ForTargetVariant) {
9197
llvm::Triple TT = LangOpts.Target;
98+
99+
if (ForTargetVariant) {
100+
assert(LangOpts.TargetVariant && "Must have target variant triple");
101+
TT = *LangOpts.TargetVariant;
102+
}
103+
92104
return isPlatformActiveForTarget(Platform, TT,
93105
LangOpts.EnableAppExtensionRestrictions);
94106
}
@@ -113,10 +125,33 @@ PlatformKind swift::targetPlatform(LangOptions &LangOpts) {
113125
}
114126

115127
if (LangOpts.Target.isiOS()) {
128+
if (tripleIsMacCatalystEnvironment(LangOpts.Target))
129+
return (LangOpts.EnableAppExtensionRestrictions
130+
? PlatformKind::macCatalystApplicationExtension
131+
: PlatformKind::macCatalyst);
116132
return (LangOpts.EnableAppExtensionRestrictions
117133
? PlatformKind::iOSApplicationExtension
118134
: PlatformKind::iOS);
119135
}
120136

121137
return PlatformKind::none;
122138
}
139+
140+
bool swift::inheritsAvailabilityFromPlatform(PlatformKind Child,
141+
PlatformKind Parent) {
142+
if (Child == PlatformKind::macCatalyst && Parent == PlatformKind::iOS)
143+
return true;
144+
145+
if (Child == PlatformKind::macCatalystApplicationExtension) {
146+
if (Parent == PlatformKind::iOS ||
147+
Parent == PlatformKind::iOSApplicationExtension ||
148+
Parent == PlatformKind::macCatalyst) {
149+
return true;
150+
}
151+
}
152+
153+
// Ideally we would have all ApplicationExtension platforms
154+
// inherit from their non-extension platform.
155+
156+
return false;
157+
}

lib/ClangImporter/ClangImporter.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1859,6 +1859,8 @@ PlatformAvailability::PlatformAvailability(LangOptions &langOpts)
18591859
switch (platformKind) {
18601860
case PlatformKind::iOS:
18611861
case PlatformKind::iOSApplicationExtension:
1862+
case PlatformKind::macCatalyst:
1863+
case PlatformKind::macCatalystApplicationExtension:
18621864
case PlatformKind::tvOS:
18631865
case PlatformKind::tvOSApplicationExtension:
18641866
deprecatedAsUnavailableMessage =
@@ -1893,6 +1895,11 @@ bool PlatformAvailability::isPlatformRelevant(StringRef name) const {
18931895
case PlatformKind::iOSApplicationExtension:
18941896
return name == "ios" || name == "ios_app_extension";
18951897

1898+
case PlatformKind::macCatalyst:
1899+
case PlatformKind::macCatalystApplicationExtension:
1900+
// ClangImporter does not yet support macCatalyst.
1901+
return false;
1902+
18961903
case PlatformKind::tvOS:
18971904
return name == "tvos";
18981905
case PlatformKind::tvOSApplicationExtension:
@@ -1933,6 +1940,11 @@ bool PlatformAvailability::treatDeprecatedAsUnavailable(
19331940
// Anything deprecated in iOS 7.x and earlier is unavailable in Swift.
19341941
return major <= 7;
19351942

1943+
case PlatformKind::macCatalyst:
1944+
case PlatformKind::macCatalystApplicationExtension:
1945+
// ClangImporter does not yet support macCatalyst.
1946+
return false;
1947+
19361948
case PlatformKind::watchOS:
19371949
case PlatformKind::watchOSApplicationExtension:
19381950
// No deprecation filter on watchOS

lib/PrintAsObjC/DeclAndTypePrinter.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,9 @@ class DeclAndTypePrinter::Implementation
827827
case PlatformKind::iOS:
828828
plat = "ios";
829829
break;
830+
case PlatformKind::macCatalyst:
831+
plat = "maccatalyst";
832+
break;
830833
case PlatformKind::tvOS:
831834
plat = "tvos";
832835
break;
@@ -839,6 +842,9 @@ class DeclAndTypePrinter::Implementation
839842
case PlatformKind::iOSApplicationExtension:
840843
plat = "ios_app_extension";
841844
break;
845+
case PlatformKind::macCatalystApplicationExtension:
846+
plat = "maccatalyst_app_extension";
847+
break;
842848
case PlatformKind::tvOSApplicationExtension:
843849
plat = "tvos_app_extension";
844850
break;

lib/Sema/TypeCheckAttr.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,6 +1351,16 @@ void AttributeChecker::visitAvailableAttr(AvailableAttr *attr) {
13511351
return;
13521352
}
13531353

1354+
// Make sure there isn't a more specific attribute we should be using instead.
1355+
// findMostSpecificActivePlatform() is O(N), so only do this if we're checking
1356+
// an iOS attribute while building for macCatalyst.
1357+
if (attr->Platform == PlatformKind::iOS &&
1358+
isPlatformActive(PlatformKind::macCatalyst, Ctx.LangOpts)) {
1359+
if (attr != D->getAttrs().findMostSpecificActivePlatform(Ctx)) {
1360+
return;
1361+
}
1362+
}
1363+
13541364
SourceLoc attrLoc = attr->getLocation();
13551365

13561366
Optional<Diag<>> MaybeNotAllowed =

0 commit comments

Comments
 (0)