Skip to content

Commit 5d91d02

Browse files
authored
Merge pull request #83280 from tshortli/print-as-clang-custom-availability-domains
PrintAsClang: Add support for custom availability domains
2 parents c472c22 + 6f3e9e7 commit 5d91d02

File tree

9 files changed

+424
-11
lines changed

9 files changed

+424
-11
lines changed

include/swift/PrintAsClang/ClangMacros.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ CLANG_MACRO("SWIFT_UNAVAILABLE", , "__attribute__((unavailable))")
220220
CLANG_MACRO("SWIFT_UNAVAILABLE_MSG", "(msg)", "__attribute__((unavailable(msg)))")
221221

222222
CLANG_MACRO("SWIFT_AVAILABILITY", "(plat, ...)", "__attribute__((availability(plat, __VA_ARGS__)))")
223+
CLANG_MACRO("SWIFT_AVAILABILITY_DOMAIN", "(dom, ...)", "__attribute__((availability(domain: dom, __VA_ARGS__)))")
223224

224225
CLANG_MACRO("SWIFT_WEAK_IMPORT", , "__attribute__((weak_import))")
225226

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,9 +1702,24 @@ class DeclAndTypePrinter::Implementation
17021702
};
17031703

17041704
for (auto AvAttr : D->getSemanticAvailableAttrs()) {
1705-
if (AvAttr.getPlatform() == PlatformKind::none) {
1706-
if (AvAttr.isUnconditionallyUnavailable() &&
1707-
!AvAttr.getDomain().isSwiftLanguage()) {
1705+
auto domain = AvAttr.getDomain();
1706+
if (auto domainDecl = domain.getDecl()) {
1707+
// If the domain is defined in a Clang module then the attr can be
1708+
// printed.
1709+
if (domainDecl->hasClangNode()) {
1710+
// Versioned custom domains aren't supported yet.
1711+
ASSERT(!domain.isVersioned());
1712+
1713+
maybePrintLeadingSpace();
1714+
os << "SWIFT_AVAILABILITY_DOMAIN("
1715+
<< domain.getNameForAttributePrinting() << ","
1716+
<< (AvAttr.isUnconditionallyUnavailable() ? "1" : "0") << ")";
1717+
}
1718+
continue;
1719+
}
1720+
1721+
if (domain.isUniversal()) {
1722+
if (AvAttr.isUnconditionallyUnavailable()) {
17081723
// Availability for *
17091724
if (!AvAttr.getRename().empty() && isa<ValueDecl>(D)) {
17101725
// rename
@@ -1749,6 +1764,9 @@ class DeclAndTypePrinter::Implementation
17491764
}
17501765

17511766
// Availability for a specific platform
1767+
if (!domain.isPlatform())
1768+
continue;
1769+
17521770
if (!AvAttr.getIntroduced().has_value() &&
17531771
!AvAttr.getDeprecated().has_value() &&
17541772
!AvAttr.getObsoleted().has_value() &&

lib/PrintAsClang/ModuleContentsWriter.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,8 @@ class ModuleWriter {
684684
continue;
685685
}
686686

687+
addImportsForReferencedAvailabilityDomains(member);
688+
687689
bool needsToBeIndividuallyDelayed = false;
688690
ReferencedTypeFinder::walk(VD->getInterfaceType(),
689691
[&](ReferencedTypeFinder &finder,
@@ -745,6 +747,15 @@ class ModuleWriter {
745747
return !hadAnyDelayedMembers;
746748
}
747749

750+
void addImportsForReferencedAvailabilityDomains(const Decl *D) {
751+
for (auto attr : D->getSemanticAvailableAttrs()) {
752+
if (auto *domainDecl = attr.getDomain().getDecl()) {
753+
if (domainDecl->hasClangNode())
754+
addImport(domainDecl);
755+
}
756+
}
757+
}
758+
748759
bool writeClass(const ClassDecl *CD) {
749760
if (addImport(CD))
750761
return true;
@@ -777,6 +788,7 @@ class ModuleWriter {
777788
if (outputLangMode == OutputLanguageMode::Cxx &&
778789
(inserted || !it->second.second))
779790
ClangValueTypePrinter::forwardDeclType(os, CD, printer);
791+
addImportsForReferencedAvailabilityDomains(CD);
780792
it->second = {EmissionState::Defined, true};
781793
printer.print(CD);
782794
return true;
@@ -795,6 +807,7 @@ class ModuleWriter {
795807
TD);
796808
forwardDeclareType(TD);
797809
});
810+
addImportsForReferencedAvailabilityDomains(FD);
798811

799812
printer.print(FD);
800813
return true;
@@ -811,6 +824,7 @@ class ModuleWriter {
811824
}
812825
forwardDeclareCxxValueTypeIfNeeded(SD);
813826
}
827+
addImportsForReferencedAvailabilityDomains(SD);
814828
printer.print(SD);
815829
return true;
816830
}
@@ -837,6 +851,8 @@ class ModuleWriter {
837851
if (!forwardDeclareMemberTypes(PD->getAllMembers(), PD))
838852
return false;
839853

854+
addImportsForReferencedAvailabilityDomains(PD);
855+
840856
seenTypes[PD] = { EmissionState::Defined, true };
841857
printer.print(PD);
842858
return true;
@@ -863,6 +879,8 @@ class ModuleWriter {
863879
if (!forwardDeclareMemberTypes(ED->getAllMembers(), ED))
864880
return false;
865881

882+
addImportsForReferencedAvailabilityDomains(ED);
883+
866884
printer.print(ED);
867885
return true;
868886
}
@@ -882,7 +900,9 @@ class ModuleWriter {
882900

883901
if (seenTypes[ED].first == EmissionState::Defined)
884902
return true;
885-
903+
904+
addImportsForReferencedAvailabilityDomains(ED);
905+
886906
seenTypes[ED] = {EmissionState::Defined, true};
887907
printer.print(ED);
888908

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,70 @@
11
#include <feature-availability.h>
22

3-
static struct __AvailabilityDomain __BayBridge
3+
static struct __AvailabilityDomain bay_bridge
44
__attribute__((availability_domain(BayBridge))) = {
55
__AVAILABILITY_DOMAIN_ENABLED, 0};
6-
static struct __AvailabilityDomain __GoldenGateBridge
6+
static struct __AvailabilityDomain golden_gate_bridge
77
__attribute__((availability_domain(GoldenGateBridge))) = {
88
__AVAILABILITY_DOMAIN_DISABLED, 0};
9+
10+
#define AVAIL 0
11+
#define UNAVAIL 1
12+
13+
14+
#if __OBJC__
15+
@import Foundation;
16+
17+
__attribute__((availability(domain:BayBridge, AVAIL)))
18+
@interface BayBridgeAvailable : NSObject
19+
- (instancetype)init;
20+
@end
21+
22+
__attribute__((availability(domain:BayBridge, UNAVAIL)))
23+
@interface BayBridgeUnavailable : NSObject
24+
- (instancetype)init;
25+
@end
26+
27+
@interface ImplementMe : NSObject
28+
- (instancetype)init;
29+
- (void)availableInBayBridge __attribute__((availability(domain:BayBridge, AVAIL)));
30+
- (void)unavailableInBayBridge __attribute__((availability(domain:BayBridge, UNAVAIL)));
31+
32+
- (void)availableInGoldenGateBridge __attribute__((availability(domain:GoldenGateBridge, AVAIL)));
33+
- (void)unavailableInGoldenGateBridge __attribute__((availability(domain:GoldenGateBridge, UNAVAIL)));
34+
35+
@end
36+
37+
__attribute__((availability(domain:BayBridge, AVAIL)))
38+
@interface ImplementMeBayBridgeAvailable : NSObject
39+
- (instancetype)init;
40+
@end
41+
42+
__attribute__((availability(domain:BayBridge, UNAVAIL)))
43+
@interface ImplementMeBayBridgeUnavailable : NSObject
44+
- (instancetype)init;
45+
@end
46+
47+
__attribute__((availability(domain:GoldenGateBridge, AVAIL)))
48+
@interface ImplementMeGoldenGateBridgeAvailable : NSObject
49+
- (instancetype)init;
50+
@end
51+
52+
__attribute__((availability(domain:GoldenGateBridge, AVAIL)))
53+
@interface ImplementMeGoldenGateBridgeAvailable2 : NSObject
54+
- (instancetype)init;
55+
@end
56+
57+
__attribute__((availability(domain:GoldenGateBridge, AVAIL)))
58+
@interface ImplementMeGoldenGateBridgeAvailable3 : NSObject
59+
- (instancetype)init;
60+
@end
61+
62+
__attribute__((availability(domain:GoldenGateBridge, UNAVAIL)))
63+
@interface ImplementMeGoldenGateBridgeUnavailable : NSObject
64+
- (instancetype)init;
65+
@end
66+
67+
#endif // __OBJC__
68+
69+
#undef UNAVAIL
70+
#undef AVAIL
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify \
4+
// RUN: -import-objc-header %S/Inputs/availability_domains_bridging_header.h \
5+
// RUN: -I %S/../Inputs/custom-modules/availability-domains \
6+
// RUN: -enable-experimental-feature CustomAvailability \
7+
// RUN: %s %S/Inputs/availability_custom_domains_other.swift
8+
9+
// Re-test with the bridging header precompiled into a .pch.
10+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-pch \
11+
// RUN: -o %t/bridging-header.pch %S/Inputs/availability_domains_bridging_header.h
12+
13+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify \
14+
// RUN: -import-objc-header %t/bridging-header.pch \
15+
// RUN: -I %S/../Inputs/custom-modules/availability-domains \
16+
// RUN: -enable-experimental-feature CustomAvailability \
17+
// RUN: %s %S/Inputs/availability_custom_domains_other.swift
18+
19+
// REQUIRES: swift_feature_CustomAvailability
20+
// REQUIRES: objc_interop
21+
22+
import Oceans // re-exports Rivers
23+
24+
func testObjCClasses( // expected-note {{add '@available' attribute to enclosing global function}}
25+
_ bayBridgeAvailable: BayBridgeAvailable, // expected-error {{'BayBridgeAvailable' is only available in BayBridge}}
26+
_ bayBridgeUnavailable: BayBridgeUnavailable, // expected-error {{'BayBridgeUnavailable' is unavailable}}
27+
) { }
28+
29+
@objc @implementation
30+
extension ImplementMe {
31+
// FIXME: [availability] @available(BayBridge) should be required
32+
func availableInBayBridge() { }
33+
34+
// FIXME: [availability] Should match and suggest @available(BayBridge, unavailable)
35+
func unavailableInBayBridge() { } // expected-error {{instance method 'unavailableInBayBridge()' does not match any instance method declared in the headers for 'ImplementMe'}}
36+
// expected-note@-1 {{add 'private' or 'fileprivate'}}
37+
// expected-note@-2 {{add 'final' to define a Swift-only instance method}}
38+
39+
@available(GoldenGateBridge)
40+
func availableInGoldenGateBridge() { }
41+
42+
// FIXME: [availability] This should be accepted
43+
@available(GoldenGateBridge, unavailable)
44+
func unavailableInGoldenGateBridge() { } // expected-error {{instance method 'unavailableInGoldenGateBridge()' does not match any instance method declared in the headers for 'ImplementMe'}}
45+
// expected-note@-1 {{add 'private' or 'fileprivate'}}
46+
// expected-note@-2 {{add 'final' to define a Swift-only instance method}}
47+
}
48+
49+
@objc @implementation
50+
extension ImplementMeBayBridgeAvailable { // expected-error {{'ImplementMeBayBridgeAvailable' is only available in BayBridge}}
51+
// expected-note@-1 {{add '@available' attribute to enclosing extension}}
52+
}
53+
54+
@objc @implementation
55+
extension ImplementMeBayBridgeUnavailable { // expected-error {{'ImplementMeBayBridgeUnavailable' is unavailable}}
56+
}
57+
58+
@available(GoldenGateBridge)
59+
@objc @implementation
60+
extension ImplementMeGoldenGateBridgeAvailable {
61+
}
62+
63+
@available(BayBridge)
64+
@objc @implementation
65+
extension ImplementMeGoldenGateBridgeAvailable2 { // expected-error {{'ImplementMeGoldenGateBridgeAvailable2' is only available in GoldenGateBridge}}
66+
// expected-note@-1 {{add '@available' attribute to enclosing extension}}
67+
}
68+
69+
// FIXME: [availability] This implementation should be rejected because its less
70+
// available than the original class declaration.
71+
@available(BayBridge)
72+
@available(GoldenGateBridge)
73+
@objc @implementation
74+
extension ImplementMeGoldenGateBridgeAvailable3 {
75+
}
76+
77+
@available(GoldenGateBridge, unavailable)
78+
@objc @implementation
79+
extension ImplementMeGoldenGateBridgeUnavailable {
80+
}

test/Inputs/custom-modules/availability-domains/Oceans.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
int arctic_pred(void);
55
int pacific_pred(void);
66

7-
static struct __AvailabilityDomain __Arctic
7+
static struct __AvailabilityDomain arctic_domain
88
__attribute__((availability_domain(Arctic))) = {
99
__AVAILABILITY_DOMAIN_DYNAMIC, arctic_pred};
10-
static struct __AvailabilityDomain __Pacific
10+
static struct __AvailabilityDomain pacific_domain
1111
__attribute__((availability_domain(Pacific))) = {
1212
__AVAILABILITY_DOMAIN_DYNAMIC, pacific_pred};
1313

test/Inputs/custom-modules/availability-domains/Rivers.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include <feature-availability.h>
22

3-
static struct __AvailabilityDomain __Colorado __attribute__((
3+
static struct __AvailabilityDomain colorado_domain __attribute__((
44
availability_domain(Colorado))) = {__AVAILABILITY_DOMAIN_DISABLED, 0};
55

66
#define AVAIL 0

test/Inputs/custom-modules/availability-domains/Seas.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#include <feature-availability.h>
22

3-
static struct __AvailabilityDomain __Baltic __attribute__((
3+
static struct __AvailabilityDomain baltic_domain __attribute__((
44
availability_domain(Baltic))) = {__AVAILABILITY_DOMAIN_ENABLED, 0};
5-
static struct __AvailabilityDomain __Mediterranean __attribute__((
5+
static struct __AvailabilityDomain _mediterranean __attribute__((
66
availability_domain(Mediterranean))) = {__AVAILABILITY_DOMAIN_ENABLED, 0};
77

88
#define AVAIL 0

0 commit comments

Comments
 (0)