Skip to content

Commit 92d09f4

Browse files
committed
Extend @available to support PackageDescription
<rdar://problem/46548531> Extend @available to support PackageDescription This introduces a new private availability kind "_PackageDescription" to allow availability testing by an arbitary version that can be passed using a new command-line flag "-swiftpm-manifest-version". The semantics are exactly same as Swift version specific availability. In longer term, it maybe possible to remove this enhancement once there is a language-level availability support for 3rd party libraries. Motivation: Swift packages are configured using a Package.swift manifest file. The manifest file uses a library called PackageDescription, which contains various settings that can be configured for a package. The new additions in the PackageDescription APIs are gated behind a "tools version" that every manifest must declare. This means, packages don't automatically get access to the new APIs. They need to update their declared tools version in order to use the new API. This is basically similar to the minimum deployment target version we have for our OSes. This gating is important for allowing packages to maintain backwards compatibility. SwiftPM currently checks for API usages at runtime in order to implement this gating. This works reasonably well but can lead to a poor experience with features like code-completion and module interface generation in IDEs and editors (that use sourcekit-lsp) as SwiftPM has no control over these features.
1 parent 9536d40 commit 92d09f4

23 files changed

+370
-93
lines changed

include/swift/AST/Attr.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,10 @@ enum class PlatformAgnosticAvailabilityKind {
616616
/// The declaration is available in some but not all versions
617617
/// of Swift, as specified by the VersionTuple members.
618618
SwiftVersionSpecific,
619+
/// The declaration is available in some but not all versions
620+
/// of SwiftPM's PackageDescription library, as specified by
621+
/// the VersionTuple members.
622+
PackageDescriptionVersionSpecific,
619623
/// The declaration is unavailable for other reasons.
620624
Unavailable,
621625
};
@@ -686,6 +690,9 @@ class AvailableAttr : public DeclAttribute {
686690
/// Whether this is a language-version-specific entity.
687691
bool isLanguageVersionSpecific() const;
688692

693+
/// Whether this is a PackageDescription version specific entity.
694+
bool isPackageDescriptionVersionSpecific() const;
695+
689696
/// Whether this is an unconditionally unavailable entity.
690697
bool isUnconditionallyUnavailable() const;
691698

@@ -722,6 +729,12 @@ class AvailableAttr : public DeclAttribute {
722729
/// Returns true if this attribute is active given the current platform.
723730
bool isActivePlatform(const ASTContext &ctx) const;
724731

732+
/// Returns the active version from the AST context corresponding to
733+
/// the available kind. For example, this will return the effective language
734+
/// version for swift version-specific availability kind, PackageDescription
735+
/// version for PackageDescription version-specific availability.
736+
llvm::VersionTuple getActiveVersion(const ASTContext &ctx) const;
737+
725738
/// Compare this attribute's version information against the platform or
726739
/// language version (assuming the this attribute pertains to the active
727740
/// platform).

include/swift/AST/AvailabilitySpec.h

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ enum class AvailabilitySpecKind {
3737

3838
/// A language-version constraint of the form "swift X.Y.Z"
3939
LanguageVersionConstraint,
40+
41+
/// A PackageDescription version constraint of the form "_PackageDescription X.Y.Z"
42+
PackageDescriptionVersionConstraint,
4043
};
4144

4245
/// The root class for specifications of API availability in availability
@@ -101,38 +104,48 @@ class PlatformVersionConstraintAvailabilitySpec : public AvailabilitySpec {
101104
};
102105

103106
/// An availability specification that guards execution based on the
104-
/// compile-time language version, e.g., swift >= 3.0.1.
105-
class LanguageVersionConstraintAvailabilitySpec : public AvailabilitySpec {
106-
SourceLoc SwiftLoc;
107+
/// compile-time platform agnostic version, e.g., swift >= 3.0.1,
108+
/// package-description >= 4.0.
109+
class PlatformAgnosticVersionConstraintAvailabilitySpec : public AvailabilitySpec {
110+
SourceLoc PlatformAgnosticNameLoc;
107111

108112
llvm::VersionTuple Version;
109113
SourceRange VersionSrcRange;
110114

111115
public:
112-
LanguageVersionConstraintAvailabilitySpec(SourceLoc SwiftLoc,
113-
llvm::VersionTuple Version,
114-
SourceRange VersionSrcRange)
115-
: AvailabilitySpec(AvailabilitySpecKind::LanguageVersionConstraint),
116-
SwiftLoc(SwiftLoc), Version(Version),
117-
VersionSrcRange(VersionSrcRange) {}
116+
PlatformAgnosticVersionConstraintAvailabilitySpec(
117+
AvailabilitySpecKind AvailabilitySpecKind,
118+
SourceLoc PlatformAgnosticNameLoc, llvm::VersionTuple Version,
119+
SourceRange VersionSrcRange)
120+
: AvailabilitySpec(AvailabilitySpecKind),
121+
PlatformAgnosticNameLoc(PlatformAgnosticNameLoc), Version(Version),
122+
VersionSrcRange(VersionSrcRange) {
123+
assert(AvailabilitySpecKind == AvailabilitySpecKind::LanguageVersionConstraint ||
124+
AvailabilitySpecKind == AvailabilitySpecKind::PackageDescriptionVersionConstraint);
125+
}
118126

119-
SourceLoc getSwiftLoc() const { return SwiftLoc; }
127+
SourceLoc getPlatformAgnosticNameLoc() const { return PlatformAgnosticNameLoc; }
120128

121129
// The platform version to compare against.
122130
llvm::VersionTuple getVersion() const { return Version; }
123131
SourceRange getVersionSrcRange() const { return VersionSrcRange; }
124132

125133
SourceRange getSourceRange() const;
126134

135+
bool isLanguageVersionSpecific() const {
136+
return getKind() == AvailabilitySpecKind::LanguageVersionConstraint;
137+
}
138+
127139
void print(raw_ostream &OS, unsigned Indent) const;
128140

129141
static bool classof(const AvailabilitySpec *Spec) {
130-
return Spec->getKind() == AvailabilitySpecKind::LanguageVersionConstraint;
142+
return Spec->getKind() == AvailabilitySpecKind::LanguageVersionConstraint ||
143+
Spec->getKind() == AvailabilitySpecKind::PackageDescriptionVersionConstraint;
131144
}
132145

133146
void *
134147
operator new(size_t Bytes, ASTContext &C,
135-
unsigned Alignment = alignof(LanguageVersionConstraintAvailabilitySpec)){
148+
unsigned Alignment = alignof(PlatformAgnosticVersionConstraintAvailabilitySpec)){
136149
return AvailabilitySpec::operator new(Bytes, C, Alignment);
137150
}
138151
};

include/swift/AST/DiagnosticsParse.def

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,15 +1367,15 @@ ERROR(attr_availability_expected_equal,none,
13671367
ERROR(attr_availability_expected_version,none,
13681368
"expected version number in '%0' attribute", (StringRef))
13691369

1370-
WARNING(attr_availability_swift_expected_option,none,
1370+
WARNING(attr_availability_platform_agnostic_expected_option,none,
13711371
"expected 'introduced', 'deprecated', or 'obsoleted' in '%0' attribute "
1372-
"for platform 'swift'", (StringRef))
1373-
WARNING(attr_availability_swift_expected_deprecated_version,none,
1372+
"for platform '%1'", (StringRef, StringRef))
1373+
WARNING(attr_availability_platform_agnostic_expected_deprecated_version,none,
13741374
"expected version number with 'deprecated' in '%0' attribute for "
1375-
"platform 'swift'", (StringRef))
1376-
WARNING(attr_availability_swift_infeasible_option,none,
1377-
"'%0' cannot be used in '%1' attribute for platform 'swift'",
1378-
(StringRef, StringRef))
1375+
"platform '%1'", (StringRef, StringRef))
1376+
WARNING(attr_availability_platform_agnostic_infeasible_option,none,
1377+
"'%0' cannot be used in '%1' attribute for platform '%2'",
1378+
(StringRef, StringRef, StringRef))
13791379

13801380
WARNING(attr_availability_nonspecific_platform_unexpected_version,none,
13811381
"unexpected version number in '%0' attribute for non-specific platform "
@@ -1610,12 +1610,15 @@ ERROR(avail_query_version_comparison_not_needed,
16101610
ERROR(availability_query_wildcard_required, none,
16111611
"must handle potential future platforms with '*'", ())
16121612

1613-
ERROR(availability_swift_must_occur_alone, none,
1614-
"'swift' version-availability must be specified alone", ())
1613+
ERROR(availability_must_occur_alone, none,
1614+
"'%0' version-availability must be specified alone", (StringRef))
16151615

16161616
ERROR(pound_available_swift_not_allowed, none,
16171617
"Swift language version checks not allowed in #available(...)", ())
16181618

1619+
ERROR(pound_available_package_description_not_allowed, none,
1620+
"PackageDescription version checks not allowed in #available(...)", ())
1621+
16191622
ERROR(availability_query_repeated_platform, none,
16201623
"version for '%0' already specified", (StringRef))
16211624

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3981,9 +3981,9 @@ NOTE(availability_marked_unavailable, none,
39813981
"%select{getter for |setter for |}0%1 has been explicitly marked "
39823982
"unavailable here", (unsigned, DeclName))
39833983

3984-
NOTE(availability_introduced_in_swift, none,
3985-
"%select{getter for |setter for |}0%1 was introduced in Swift %2",
3986-
(unsigned, DeclName, llvm::VersionTuple))
3984+
NOTE(availability_introduced_in_version, none,
3985+
"%select{getter for |setter for |}0%1 was introduced in %2 %3",
3986+
(unsigned, DeclName, StringRef, llvm::VersionTuple))
39873987

39883988
NOTE(availability_obsoleted, none,
39893989
"%select{getter for |setter for |}0%1 was obsoleted in %2 %3",

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ namespace swift {
8282
/// User-overridable language version to compile for.
8383
version::Version EffectiveLanguageVersion = version::Version::getCurrentLanguageVersion();
8484

85+
/// PackageDescription version to compile for.
86+
version::Version PackageDescriptionVersion;
87+
8588
/// Disable API availability checking.
8689
bool DisableAvailabilityChecking = false;
8790

include/swift/Option/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,11 @@ def swift_version : Separate<["-"], "swift-version">, Flags<[FrontendOption, Par
172172
HelpText<"Interpret input according to a specific Swift language version number">,
173173
MetaVarName<"<vers>">;
174174

175+
def package_description_version: Separate<["-"], "package-description-version">,
176+
Flags<[FrontendOption, HelpHidden, ParseableInterfaceOption]>,
177+
HelpText<"The version number to be applied on the input for the PackageDescription availability kind">,
178+
MetaVarName<"<vers>">;
179+
175180
def tools_directory : Separate<["-"], "tools-directory">,
176181
Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild,
177182
ArgumentIsPath]>,

include/swift/Parse/Parser.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,8 +1414,8 @@ class Parser {
14141414
ParserResult<AvailabilitySpec> parseAvailabilitySpec();
14151415
ParserResult<PlatformVersionConstraintAvailabilitySpec>
14161416
parsePlatformVersionConstraintSpec();
1417-
ParserResult<LanguageVersionConstraintAvailabilitySpec>
1418-
parseLanguageVersionConstraintSpec();
1417+
ParserResult<PlatformAgnosticVersionConstraintAvailabilitySpec>
1418+
parsePlatformAgnosticVersionConstraintSpec();
14191419

14201420
bool canDelayMemberDeclParsing();
14211421
};

include/swift/Serialization/ModuleFormat.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5252
/// describe what change you made. The content of this comment isn't important;
5353
/// it just ensures a conflict if two people change the module format.
5454
/// Don't worry about adhering to the 80-column limit for this line.
55-
const uint16_t SWIFTMODULE_VERSION_MINOR = 470; // Last change: Remove @trivial
55+
const uint16_t SWIFTMODULE_VERSION_MINOR = 471; // Last change: Add @available(_PackageDescription..)
5656

5757
using DeclIDField = BCFixed<31>;
5858

@@ -1547,6 +1547,7 @@ namespace decls_block {
15471547
BCFixed<1>, // implicit flag
15481548
BCFixed<1>, // is unconditionally unavailable?
15491549
BCFixed<1>, // is unconditionally deprecated?
1550+
BCFixed<1>, // is this PackageDescription version-specific kind?
15501551
BC_AVAIL_TUPLE, // Introduced
15511552
BC_AVAIL_TUPLE, // Deprecated
15521553
BC_AVAIL_TUPLE, // Obsoleted

lib/AST/ASTDumper.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1437,7 +1437,8 @@ class PrintStmt : public StmtVisitor<PrintStmt> {
14371437
cast<PlatformVersionConstraintAvailabilitySpec>(Query)->print(OS, Indent + 2);
14381438
break;
14391439
case AvailabilitySpecKind::LanguageVersionConstraint:
1440-
cast<LanguageVersionConstraintAvailabilitySpec>(Query)->print(OS, Indent + 2);
1440+
case AvailabilitySpecKind::PackageDescriptionVersionConstraint:
1441+
cast<PlatformVersionConstraintAvailabilitySpec>(Query)->print(OS, Indent + 2);
14411442
break;
14421443
case AvailabilitySpecKind::OtherPlatform:
14431444
cast<OtherPlatformAvailabilitySpec>(Query)->print(OS, Indent + 2);

lib/AST/Attr.cpp

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ const AvailableAttr *DeclAttributes::getUnavailable(
142142

143143
// If this attribute doesn't apply to the active platform, we're done.
144144
if (!AvAttr->isActivePlatform(ctx) &&
145-
!AvAttr->isLanguageVersionSpecific())
145+
!AvAttr->isLanguageVersionSpecific() &&
146+
!AvAttr->isPackageDescriptionVersionSpecific())
146147
continue;
147148

148149
// Unconditional unavailable.
@@ -172,7 +173,8 @@ DeclAttributes::getDeprecated(const ASTContext &ctx) const {
172173
continue;
173174

174175
if (!AvAttr->isActivePlatform(ctx) &&
175-
!AvAttr->isLanguageVersionSpecific())
176+
!AvAttr->isLanguageVersionSpecific() &&
177+
!AvAttr->isPackageDescriptionVersionSpecific())
176178
continue;
177179

178180
// Unconditional deprecated.
@@ -183,10 +185,7 @@ DeclAttributes::getDeprecated(const ASTContext &ctx) const {
183185
if (!DeprecatedVersion.hasValue())
184186
continue;
185187

186-
llvm::VersionTuple MinVersion =
187-
AvAttr->isLanguageVersionSpecific() ?
188-
ctx.LangOpts.EffectiveLanguageVersion :
189-
ctx.LangOpts.getMinPlatformVersion();
188+
llvm::VersionTuple MinVersion = AvAttr->getActiveVersion(ctx);
190189

191190
// We treat the declaration as deprecated if it is deprecated on
192191
// all deployment targets.
@@ -240,6 +239,7 @@ static bool isShortAvailable(const DeclAttribute *DA) {
240239
return false;
241240
case PlatformAgnosticAvailabilityKind::None:
242241
case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific:
242+
case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific:
243243
return true;
244244
}
245245

@@ -261,10 +261,16 @@ static void printShortFormAvailable(ArrayRef<const DeclAttribute *> Attrs,
261261
Printer << "@available(";
262262
auto FirstAvail = cast<AvailableAttr>(Attrs.front());
263263
if (Attrs.size() == 1 &&
264-
FirstAvail->isLanguageVersionSpecific()) {
264+
FirstAvail->getPlatformAgnosticAvailability() !=
265+
PlatformAgnosticAvailabilityKind::None) {
265266
assert(FirstAvail->Introduced.hasValue());
266-
Printer << "swift "
267-
<< FirstAvail->Introduced.getValue().getAsString()
267+
if (FirstAvail->isLanguageVersionSpecific()) {
268+
Printer << "swift ";
269+
} else {
270+
assert(FirstAvail->isPackageDescriptionVersionSpecific());
271+
Printer << "_PackageDescription ";
272+
}
273+
Printer << FirstAvail->Introduced.getValue().getAsString()
268274
<< ")";
269275
} else {
270276
for (auto *DA : Attrs) {
@@ -290,6 +296,7 @@ void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options,
290296
// Process attributes in passes.
291297
AttributeVector shortAvailableAttributes;
292298
const DeclAttribute *swiftVersionAvailableAttribute = nullptr;
299+
const DeclAttribute *packageDescriptionVersionAvailableAttribute = nullptr;
293300
AttributeVector longAttributes;
294301
AttributeVector attributes;
295302
AttributeVector modifiers;
@@ -311,6 +318,11 @@ void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options,
311318
swiftVersionAvailableAttribute = availableAttr;
312319
continue;
313320
}
321+
if (availableAttr->isPackageDescriptionVersionSpecific() &&
322+
isShortAvailable(availableAttr)) {
323+
packageDescriptionVersionAvailableAttribute = availableAttr;
324+
continue;
325+
}
314326
}
315327

316328
AttributeVector &which = DA->isDeclModifier() ? modifiers :
@@ -322,6 +334,8 @@ void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options,
322334

323335
if (swiftVersionAvailableAttribute)
324336
printShortFormAvailable(swiftVersionAvailableAttribute, Printer, Options);
337+
if (packageDescriptionVersionAvailableAttribute)
338+
printShortFormAvailable(packageDescriptionVersionAvailableAttribute, Printer, Options);
325339
if (!shortAvailableAttributes.empty())
326340
printShortFormAvailable(shortAvailableAttributes, Printer, Options);
327341

@@ -424,6 +438,8 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
424438
auto Attr = cast<AvailableAttr>(this);
425439
if (Attr->isLanguageVersionSpecific())
426440
Printer << "swift";
441+
else if (Attr->isPackageDescriptionVersionSpecific())
442+
Printer << "_PackageDescription";
427443
else
428444
Printer << Attr->platformString();
429445

@@ -890,11 +906,25 @@ bool AvailableAttr::isLanguageVersionSpecific() const {
890906
return false;
891907
}
892908

909+
bool AvailableAttr::isPackageDescriptionVersionSpecific() const {
910+
if (PlatformAgnostic ==
911+
PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific)
912+
{
913+
assert(Platform == PlatformKind::none &&
914+
(Introduced.hasValue() ||
915+
Deprecated.hasValue() ||
916+
Obsoleted.hasValue()));
917+
return true;
918+
}
919+
return false;
920+
}
921+
893922
bool AvailableAttr::isUnconditionallyUnavailable() const {
894923
switch (PlatformAgnostic) {
895924
case PlatformAgnosticAvailabilityKind::None:
896925
case PlatformAgnosticAvailabilityKind::Deprecated:
897926
case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific:
927+
case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific:
898928
return false;
899929

900930
case PlatformAgnosticAvailabilityKind::Unavailable:
@@ -911,6 +941,7 @@ bool AvailableAttr::isUnconditionallyDeprecated() const {
911941
case PlatformAgnosticAvailabilityKind::Unavailable:
912942
case PlatformAgnosticAvailabilityKind::UnavailableInSwift:
913943
case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific:
944+
case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific:
914945
return false;
915946

916947
case PlatformAgnosticAvailabilityKind::Deprecated:
@@ -920,17 +951,24 @@ bool AvailableAttr::isUnconditionallyDeprecated() const {
920951
llvm_unreachable("Unhandled PlatformAgnosticAvailabilityKind in switch.");
921952
}
922953

954+
llvm::VersionTuple AvailableAttr::getActiveVersion(const ASTContext &ctx) const {
955+
if (isLanguageVersionSpecific()) {
956+
return ctx.LangOpts.EffectiveLanguageVersion;
957+
} else if (isPackageDescriptionVersionSpecific()) {
958+
return ctx.LangOpts.PackageDescriptionVersion;
959+
} else {
960+
return ctx.LangOpts.getMinPlatformVersion();
961+
}
962+
}
963+
923964
AvailableVersionComparison AvailableAttr::getVersionAvailability(
924965
const ASTContext &ctx) const {
925966

926967
// Unconditional unavailability.
927968
if (isUnconditionallyUnavailable())
928969
return AvailableVersionComparison::Unavailable;
929970

930-
llvm::VersionTuple queryVersion =
931-
isLanguageVersionSpecific() ?
932-
ctx.LangOpts.EffectiveLanguageVersion :
933-
ctx.LangOpts.getMinPlatformVersion();
971+
llvm::VersionTuple queryVersion = getActiveVersion(ctx);
934972

935973
// If this entity was obsoleted before or at the query platform version,
936974
// consider it obsolete.
@@ -943,7 +981,7 @@ AvailableVersionComparison AvailableAttr::getVersionAvailability(
943981
// static requirement, so we treat "introduced later" as just plain
944982
// unavailable.
945983
if (Introduced && *Introduced > queryVersion) {
946-
if (isLanguageVersionSpecific())
984+
if (isLanguageVersionSpecific() || isPackageDescriptionVersionSpecific())
947985
return AvailableVersionComparison::Unavailable;
948986
else
949987
return AvailableVersionComparison::PotentiallyUnavailable;

0 commit comments

Comments
 (0)