Skip to content

Commit 610a4a5

Browse files
authored
Merge pull request #41692 from nkcsgexi/spi-available
2 parents 9e78702 + 43a8482 commit 610a4a5

24 files changed

+188
-36
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,3 +830,10 @@ the compiler.
830830

831831
This `async` function uses the pre-SE-0338 semantics of unsafely inheriting the caller's executor. This is an underscored feature because the right way of inheriting an executor is to pass in the required executor and switch to it. Unfortunately, there are functions in the standard library which need to inherit their caller's executor but cannot change their ABI because they were not defined as `@_alwaysEmitIntoClient` in the initial release.
832832

833+
## `@_spi_available(platform, version)`
834+
835+
Like `@available`, this attribute indicates a decl is available only as an SPI.
836+
This implies several behavioral changes comparing to regular `@available`:
837+
1. Type checker diagnoses when a client accidently exposes such a symbol in library APIs.
838+
2. When emitting public interfaces, `@_spi_available` is printed as `@available(platform, unavailable)`.
839+
3. ClangImporter imports ObjC macros `SPI_AVAILABLE` and `__SPI_AVAILABLE` to this attribute.

include/swift/AST/Attr.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -632,14 +632,16 @@ class AvailableAttr : public DeclAttribute {
632632
const llvm::VersionTuple &Obsoleted,
633633
SourceRange ObsoletedRange,
634634
PlatformAgnosticAvailabilityKind PlatformAgnostic,
635-
bool Implicit)
635+
bool Implicit,
636+
bool IsSPI)
636637
: DeclAttribute(DAK_Available, AtLoc, Range, Implicit),
637638
Message(Message), Rename(Rename), RenameDecl(RenameDecl),
638639
INIT_VER_TUPLE(Introduced), IntroducedRange(IntroducedRange),
639640
INIT_VER_TUPLE(Deprecated), DeprecatedRange(DeprecatedRange),
640641
INIT_VER_TUPLE(Obsoleted), ObsoletedRange(ObsoletedRange),
641642
PlatformAgnostic(PlatformAgnostic),
642-
Platform(Platform)
643+
Platform(Platform),
644+
IsSPI(IsSPI)
643645
{}
644646

645647
#undef INIT_VER_TUPLE
@@ -685,6 +687,9 @@ class AvailableAttr : public DeclAttribute {
685687
/// The platform of the availability.
686688
const PlatformKind Platform;
687689

690+
/// Whether this is available as SPI.
691+
const bool IsSPI;
692+
688693
/// Whether this is a language-version-specific entity.
689694
bool isLanguageVersionSpecific() const;
690695

include/swift/AST/Availability.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,12 @@ class UnavailabilityReason {
209209
/// [lattice]: http://mathworld.wolfram.com/Lattice.html
210210
class AvailabilityContext {
211211
VersionRange OSVersion;
212+
llvm::Optional<bool> SPI;
212213
public:
213214
/// Creates a context that requires certain versions of the target OS.
214-
explicit AvailabilityContext(VersionRange OSVersion) : OSVersion(OSVersion) {}
215+
explicit AvailabilityContext(VersionRange OSVersion,
216+
llvm::Optional<bool> SPI = llvm::None)
217+
: OSVersion(OSVersion), SPI(SPI) {}
215218

216219
/// Creates a context that imposes the constraints of the ASTContext's
217220
/// deployment target.
@@ -291,6 +294,10 @@ class AvailabilityContext {
291294
void unionWith(AvailabilityContext other) {
292295
OSVersion.unionWith(other.getOSVersion());
293296
}
297+
298+
bool isAvailableAsSPI() const {
299+
return SPI && *SPI;
300+
}
294301
};
295302

296303

include/swift/AST/Decl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,8 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
10301030
// an @_spi context.
10311031
bool isSPI() const;
10321032

1033+
bool isAvailableAsSPI() const;
1034+
10331035
// List the SPI groups declared with @_spi or inherited by this decl.
10341036
//
10351037
// SPI groups are inherited from the parent contexts only if the local decl

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,6 +1542,9 @@ ERROR(optional_attribute_initializer,none,
15421542
ERROR(unavailable_method_non_objc_protocol,none,
15431543
"protocol members can only be marked unavailable in an @objc protocol",
15441544
())
1545+
ERROR(spi_available_malformed,none,
1546+
"SPI available only supports introducing version on specific platform",
1547+
())
15451548
ERROR(missing_in_class_init_1,none,
15461549
"stored property %0 requires an initial value%select{| or should be "
15471550
"@NSManaged}1", (Identifier, bool))

include/swift/Strings.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ constexpr static const StringLiteral BUILTIN_TYPE_NAME_PREFIX = "Builtin.";
6464
constexpr static const StringLiteral CLANG_MODULE_DEFUALT_SPI_GROUP_NAME =
6565
"OBJC_DEFAULT_SPI_GROUP";
6666

67+
/// The attribute name for @_spi_available
68+
constexpr static const StringLiteral SPI_AVAILABLE_ATTRNAME =
69+
"_spi_available";
70+
6771
/// A composition class containing a StringLiteral for the names of
6872
/// Swift builtins. The reason we use this is to ensure that we when
6973
/// necessary slice off the "Builtin." prefix from these names in a

lib/AST/Attr.cpp

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "swift/AST/Types.h"
3030
#include "swift/Basic/Defer.h"
3131
#include "swift/Basic/QuotedString.h"
32+
#include "swift/Strings.h"
3233
#include "llvm/ADT/SmallString.h"
3334
#include "llvm/ADT/StringSwitch.h"
3435
#include "llvm/Support/ErrorHandling.h"
@@ -126,6 +127,7 @@ DeclAttrKind DeclAttribute::getAttrKindFromString(StringRef Str) {
126127
#define DECL_ATTR(X, CLASS, ...) .Case(#X, DAK_##CLASS)
127128
#define DECL_ATTR_ALIAS(X, CLASS) .Case(#X, DAK_##CLASS)
128129
#include "swift/AST/Attr.def"
130+
.Case(SPI_AVAILABLE_ATTRNAME, DAK_Available)
129131
.Default(DAK_Count);
130132
}
131133

@@ -370,6 +372,9 @@ static bool isShortAvailable(const DeclAttribute *DA) {
370372
if (!AvailAttr)
371373
return false;
372374

375+
if (AvailAttr->IsSPI)
376+
return false;
377+
373378
if (!AvailAttr->Introduced.hasValue())
374379
return false;
375380

@@ -968,9 +973,23 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
968973
}
969974

970975
case DAK_Available: {
971-
Printer.printAttrName("@available");
972-
Printer << "(";
973976
auto Attr = cast<AvailableAttr>(this);
977+
if (!Options.PrintSPIs && Attr->IsSPI) {
978+
assert(Attr->hasPlatform());
979+
assert(Attr->Introduced.hasValue());
980+
Printer.printAttrName("@available");
981+
Printer << "(";
982+
Printer << Attr->platformString();
983+
Printer << ", unavailable)";
984+
break;
985+
}
986+
if (Attr->IsSPI) {
987+
std::string atSPI = (llvm::Twine("@") + SPI_AVAILABLE_ATTRNAME).str();
988+
Printer.printAttrName(atSPI);
989+
} else {
990+
Printer.printAttrName("@available");
991+
}
992+
Printer << "(";
974993
printAvailableAttr(Attr, Printer, Options);
975994
Printer << ")";
976995
break;
@@ -1599,7 +1618,7 @@ AvailableAttr::createPlatformAgnostic(ASTContext &C,
15991618
NoVersion, SourceRange(),
16001619
NoVersion, SourceRange(),
16011620
Obsoleted, SourceRange(),
1602-
Kind, /* isImplicit */ false);
1621+
Kind, /* isImplicit */ false, /*SPI*/false);
16031622
}
16041623

16051624
AvailableAttr *AvailableAttr::createForAlternative(
@@ -1610,7 +1629,7 @@ AvailableAttr *AvailableAttr::createForAlternative(
16101629
NoVersion, SourceRange(),
16111630
NoVersion, SourceRange(),
16121631
NoVersion, SourceRange(),
1613-
PlatformAgnosticAvailabilityKind::None, /*Implicit=*/true);
1632+
PlatformAgnosticAvailabilityKind::None, /*Implicit=*/true, /*SPI*/false);
16141633
}
16151634

16161635
bool AvailableAttr::isActivePlatform(const ASTContext &ctx) const {
@@ -1628,7 +1647,8 @@ AvailableAttr *AvailableAttr::clone(ASTContext &C, bool implicit) const {
16281647
Obsoleted ? *Obsoleted : llvm::VersionTuple(),
16291648
implicit ? SourceRange() : ObsoletedRange,
16301649
PlatformAgnostic,
1631-
implicit);
1650+
implicit,
1651+
IsSPI);
16321652
}
16331653

16341654
Optional<OriginallyDefinedInAttr::ActiveVersion>

lib/AST/Availability.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ struct InferredAvailability {
4141
Optional<llvm::VersionTuple> Introduced;
4242
Optional<llvm::VersionTuple> Deprecated;
4343
Optional<llvm::VersionTuple> Obsoleted;
44+
bool IsSPI = false;
4445
};
4546

4647
/// The type of a function that merges two version tuples.
@@ -51,17 +52,20 @@ typedef const llvm::VersionTuple &(*MergeFunction)(
5152

5253
/// Apply a merge function to two optional versions, returning the result
5354
/// in Inferred.
54-
static void
55+
static bool
5556
mergeIntoInferredVersion(const Optional<llvm::VersionTuple> &Version,
5657
Optional<llvm::VersionTuple> &Inferred,
5758
MergeFunction Merge) {
5859
if (Version.hasValue()) {
5960
if (Inferred.hasValue()) {
6061
Inferred = Merge(Inferred.getValue(), Version.getValue());
62+
return *Inferred == *Version;
6163
} else {
6264
Inferred = Version;
65+
return true;
6366
}
6467
}
68+
return false;
6569
}
6670

6771
/// Merge an attribute's availability with an existing inferred availability
@@ -75,7 +79,9 @@ static void mergeWithInferredAvailability(const AvailableAttr *Attr,
7579
static_cast<unsigned>(Attr->getPlatformAgnosticAvailability())));
7680

7781
// The merge of two introduction versions is the maximum of the two versions.
78-
mergeIntoInferredVersion(Attr->Introduced, Inferred.Introduced, std::max);
82+
if (mergeIntoInferredVersion(Attr->Introduced, Inferred.Introduced, std::max)) {
83+
Inferred.IsSPI = Attr->IsSPI;
84+
}
7985

8086
// The merge of deprecated and obsoleted versions takes the minimum.
8187
mergeIntoInferredVersion(Attr->Deprecated, Inferred.Deprecated, std::min);
@@ -103,7 +109,8 @@ createAvailableAttr(PlatformKind Platform,
103109
Introduced, /*IntroducedRange=*/SourceRange(),
104110
Deprecated, /*DeprecatedRange=*/SourceRange(),
105111
Obsoleted, /*ObsoletedRange=*/SourceRange(),
106-
Inferred.PlatformAgnostic, /*Implicit=*/true);
112+
Inferred.PlatformAgnostic, /*Implicit=*/true,
113+
Inferred.IsSPI);
107114
}
108115

109116
void AvailabilityInference::applyInferredAvailableAttrs(
@@ -173,7 +180,13 @@ AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) {
173180
return None;
174181

175182
return AvailabilityContext{
176-
VersionRange::allGTE(bestAvailAttr->Introduced.getValue())};
183+
VersionRange::allGTE(bestAvailAttr->Introduced.getValue()),
184+
bestAvailAttr->IsSPI};
185+
}
186+
187+
bool Decl::isAvailableAsSPI() const {
188+
return AvailabilityInference::availableRange(this, getASTContext())
189+
.isAvailableAsSPI();
177190
}
178191

179192
AvailabilityContext

lib/ClangImporter/ImportDecl.cpp

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1936,7 +1936,8 @@ static void applyAvailableAttribute(Decl *decl, AvailabilityContext &info,
19361936
/*Obsoleted=*/noVersion,
19371937
/*ObsoletedRange*/SourceRange(),
19381938
PlatformAgnosticAvailabilityKind::None,
1939-
/*Implicit=*/false);
1939+
/*Implicit=*/false,
1940+
/*SPI*/false);
19401941

19411942
decl->getAttrs().add(AvAttr);
19421943
}
@@ -2681,7 +2682,8 @@ namespace {
26812682
/*Deprecated*/llvm::VersionTuple(), SourceRange(),
26822683
/*Obsoleted*/llvm::VersionTuple(), SourceRange(),
26832684
PlatformAgnosticAvailabilityKind::SwiftVersionSpecific,
2684-
/*Implicit*/false);
2685+
/*Implicit*/false,
2686+
/*SPI*/false);
26852687
}
26862688
}
26872689

@@ -9013,18 +9015,10 @@ void ClangImporter::Implementation::importAttributes(
90139015
AnyUnavailable = true;
90149016
}
90159017

9016-
if (EnableClangSPI) {
9017-
if (isUsingMacroName(getClangASTContext().getSourceManager(),
9018+
auto IsSPI = isUsingMacroName(getClangASTContext().getSourceManager(),
90189019
avail->getLoc(), "SPI_AVAILABLE") ||
9019-
isUsingMacroName(getClangASTContext().getSourceManager(),
9020-
avail->getLoc(), "__SPI_AVAILABLE")) {
9021-
// The decl has been marked as SPI in the header by using the SPI macro,
9022-
// thus we add the SPI attribute to it with a default group name.
9023-
MappedDecl->getAttrs().add(SPIAccessControlAttr::create(SwiftContext,
9024-
SourceLoc(), SourceRange(),
9025-
SwiftContext.getIdentifier(CLANG_MODULE_DEFUALT_SPI_GROUP_NAME)));
9026-
}
9027-
}
9020+
isUsingMacroName(getClangASTContext().getSourceManager(),
9021+
avail->getLoc(), "__SPI_AVAILABLE");
90289022

90299023
StringRef message = avail->getMessage();
90309024

@@ -9065,7 +9059,8 @@ void ClangImporter::Implementation::importAttributes(
90659059
/*DeprecatedRange=*/SourceRange(),
90669060
obsoleted,
90679061
/*ObsoletedRange=*/SourceRange(),
9068-
PlatformAgnostic, /*Implicit=*/false);
9062+
PlatformAgnostic, /*Implicit=*/false,
9063+
IsSPI);
90699064

90709065
MappedDecl->getAttrs().add(AvAttr);
90719066
}

lib/Parse/ParseDecl.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/Parse/SyntaxParsingContext.h"
2222
#include "swift/Syntax/SyntaxKind.h"
2323
#include "swift/Subsystems.h"
24+
#include "swift/Strings.h"
2425
#include "swift/AST/ASTWalker.h"
2526
#include "swift/AST/Attr.h"
2627
#include "swift/AST/GenericParamList.h"
@@ -573,7 +574,8 @@ ParserResult<AvailableAttr> Parser::parseExtendedAvailabilitySpecList(
573574
Deprecated.Version, Deprecated.Range,
574575
Obsoleted.Version, Obsoleted.Range,
575576
PlatformAgnostic,
576-
/*Implicit=*/false);
577+
/*Implicit=*/false,
578+
AttrName == SPI_AVAILABLE_ATTRNAME);
577579
return makeParserResult(Attr);
578580

579581
}
@@ -858,7 +860,8 @@ bool Parser::parseAvailability(
858860
/*DeprecatedRange=*/SourceRange(),
859861
/*Obsoleted=*/llvm::VersionTuple(),
860862
/*ObsoletedRange=*/SourceRange(), PlatformAgnostic,
861-
/*Implicit=*/false));
863+
/*Implicit=*/false,
864+
AttrName == SPI_AVAILABLE_ATTRNAME));
862865
}
863866

864867
return true;

0 commit comments

Comments
 (0)