Skip to content

Commit d09e117

Browse files
committed
AST: Canonicalize platform versions for @backDeployed attrs.
Also, diagnose invalid platform versions. Part of rdar://155558161.
1 parent 67d784f commit d09e117

File tree

10 files changed

+84
-24
lines changed

10 files changed

+84
-24
lines changed

include/swift/AST/Attr.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2939,17 +2939,25 @@ class UnavailableFromAsyncAttr : public DeclAttribute {
29392939
/// available for back deployment to older OSes via emission into the client
29402940
/// binary.
29412941
class BackDeployedAttr : public DeclAttribute {
2942+
const PlatformKind Platform;
2943+
const llvm::VersionTuple Version;
2944+
29422945
public:
29432946
BackDeployedAttr(SourceLoc AtLoc, SourceRange Range, PlatformKind Platform,
29442947
const llvm::VersionTuple &Version, bool Implicit)
29452948
: DeclAttribute(DeclAttrKind::BackDeployed, AtLoc, Range, Implicit),
29462949
Platform(Platform), Version(Version) {}
29472950

29482951
/// The platform the decl is available for back deployment in.
2949-
const PlatformKind Platform;
2952+
PlatformKind getPlatform() const { return Platform; }
29502953

2951-
/// The earliest platform version that may use the back deployed implementation.
2952-
const llvm::VersionTuple Version;
2954+
/// The `before:` version tuple that was written in source.
2955+
llvm::VersionTuple getParsedVersion() const { return Version; }
2956+
2957+
/// The `before:` version, which is the earliest platform version that may use
2958+
/// the back deployed implementation. This may be different from the version
2959+
/// that was written in source due to canonicalization.
2960+
llvm::VersionTuple getVersion() const;
29532961

29542962
/// Returns true if this attribute is active given the current platform.
29552963
bool isActivePlatform(const ASTContext &ctx, bool forTargetVariant) const;

lib/AST/ASTDumper.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5121,9 +5121,10 @@ class PrintAttribute : public AttributeVisitor<PrintAttribute, void, Label>,
51215121
}
51225122
void visitBackDeployedAttr(BackDeployedAttr *Attr, Label label) {
51235123
printCommon(Attr, "back_deployed_attr", label);
5124-
printField(Attr->Platform, Label::always("platform"));
5125-
printFieldRaw([&](auto &out) { out << Attr->Version.getAsString(); },
5126-
Label::always("version"));
5124+
printField(Attr->getPlatform(), Label::always("platform"));
5125+
printFieldRaw(
5126+
[&](auto &out) { out << Attr->getParsedVersion().getAsString(); },
5127+
Label::always("version"));
51275128
printFoot();
51285129
}
51295130
void visitCDeclAttr(CDeclAttr *Attr, Label label) {

lib/AST/Attr.cpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -421,8 +421,9 @@ DeclAttributes::getBackDeployed(const ASTContext &ctx,
421421

422422
// We have an attribute that is active for the platform, but
423423
// is it more specific than our current best?
424-
if (!bestAttr || inheritsAvailabilityFromPlatform(
425-
backDeployedAttr->Platform, bestAttr->Platform)) {
424+
if (!bestAttr ||
425+
inheritsAvailabilityFromPlatform(backDeployedAttr->getPlatform(),
426+
bestAttr->getPlatform())) {
426427
bestAttr = backDeployedAttr;
427428
}
428429
}
@@ -557,8 +558,8 @@ static void printShortFormBackDeployed(ArrayRef<const DeclAttribute *> Attrs,
557558
if (!isFirst)
558559
Printer << ", ";
559560
auto *attr = cast<BackDeployedAttr>(DA);
560-
Printer << platformString(attr->Platform) << " "
561-
<< attr->Version.getAsString();
561+
Printer << platformString(attr->getPlatform()) << " "
562+
<< attr->getVersion().getAsString();
562563
isFirst = false;
563564
}
564565
Printer << ")";
@@ -771,7 +772,7 @@ static std::optional<PlatformKind> referencedPlatform(const DeclAttribute *attr,
771772
}
772773
return std::nullopt;
773774
case DeclAttrKind::BackDeployed:
774-
return static_cast<const BackDeployedAttr *>(attr)->Platform;
775+
return static_cast<const BackDeployedAttr *>(attr)->getPlatform();
775776
case DeclAttrKind::OriginallyDefinedIn:
776777
return static_cast<const OriginallyDefinedInAttr *>(attr)->getPlatform();
777778
default:
@@ -1519,8 +1520,8 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
15191520
Printer.printAttrName("@backDeployed");
15201521
Printer << "(before: ";
15211522
auto Attr = cast<BackDeployedAttr>(this);
1522-
Printer << platformString(Attr->Platform) << " " <<
1523-
Attr->Version.getAsString();
1523+
Printer << platformString(Attr->getPlatform()) << " "
1524+
<< Attr->getVersion().getAsString();
15241525
Printer << ")";
15251526
break;
15261527
}
@@ -2350,6 +2351,10 @@ AvailableAttr *AvailableAttr::createUnavailableInEmbedded(ASTContext &C,
23502351
/*Implicit=*/false, /*IsSPI=*/false);
23512352
}
23522353

2354+
llvm::VersionTuple BackDeployedAttr::getVersion() const {
2355+
return canonicalizePlatformVersion(getPlatform(), getParsedVersion());
2356+
}
2357+
23532358
bool BackDeployedAttr::isActivePlatform(const ASTContext &ctx,
23542359
bool forTargetVariant) const {
23552360
return isPlatformActive(Platform, ctx.LangOpts, forTargetVariant);

lib/AST/Availability.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ bool AvailabilityInference::updateBeforeAvailabilityDomainForFallback(
370370
if (!hasRemap)
371371
return false;
372372

373-
auto beforeVersion = attr->Version;
373+
auto beforeVersion = attr->getVersion();
374374
auto potentiallyRemappedIntroducedVersion =
375375
getRemappedIntroducedVersionForFallbackPlatform(ctx, beforeVersion);
376376
if (potentiallyRemappedIntroducedVersion.has_value()) {

lib/AST/Decl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ std::optional<std::pair<const BackDeployedAttr *, AvailabilityRange>>
611611
Decl::getBackDeployedAttrAndRange(ASTContext &Ctx,
612612
bool forTargetVariant) const {
613613
if (auto *attr = getAttrs().getBackDeployed(Ctx, forTargetVariant)) {
614-
auto version = attr->Version;
614+
auto version = attr->getVersion();
615615
AvailabilityDomain ignoredDomain;
616616
AvailabilityInference::updateBeforeAvailabilityDomainForFallback(
617617
attr, getASTContext(), ignoredDomain, version);

lib/Sema/TypeCheckAttr.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5122,6 +5122,13 @@ void AttributeChecker::checkBackDeployedAttrs(
51225122
ActiveAttr = AttrAndRange->first;
51235123

51245124
for (auto *Attr : Attrs) {
5125+
auto Domain = Attr->getAvailabilityDomain();
5126+
if (!Domain.isVersionValid(Attr->getParsedVersion())) {
5127+
diagnose(Attr->getLocation(),
5128+
diag::availability_invalid_version_number_for_domain,
5129+
Attr->getParsedVersion(), Domain);
5130+
}
5131+
51255132
// Back deployment only makes sense for public declarations.
51265133
if (diagnoseAndRemoveAttrIfDeclIsNonPublic(Attr, /*isError=*/true))
51275134
continue;
@@ -5169,7 +5176,7 @@ void AttributeChecker::checkBackDeployedAttrs(
51695176
}
51705177

51715178
auto AtLoc = Attr->AtLoc;
5172-
auto Platform = Attr->Platform;
5179+
auto Platform = Attr->getPlatform();
51735180

51745181
if (!seenPlatforms.insert({Platform, AtLoc}).second) {
51755182
// We've seen the platform before, emit error to the previous one which
@@ -5210,9 +5217,8 @@ void AttributeChecker::checkBackDeployedAttrs(
52105217
D->getLoc(), D->getInnermostDeclContext());
52115218

52125219
// Unavailable decls cannot be back deployed.
5213-
auto backDeployedDomain = Attr->getAvailabilityDomain();
5214-
if (availability.containsUnavailableDomain(backDeployedDomain)) {
5215-
auto domainForDiagnostics = backDeployedDomain;
5220+
if (availability.containsUnavailableDomain(Domain)) {
5221+
auto domainForDiagnostics = Domain;
52165222
llvm::VersionTuple ignoredVersion;
52175223

52185224
AvailabilityInference::updateBeforeAvailabilityDomainForFallback(
@@ -5244,7 +5250,7 @@ void AttributeChecker::checkBackDeployedAttrs(
52445250
if (auto availableRangeAttrPair =
52455251
getSemanticAvailableRangeDeclAndAttr(VD)) {
52465252
auto beforeDomain = Attr->getAvailabilityDomain();
5247-
auto beforeVersion = Attr->Version;
5253+
auto beforeVersion = Attr->getVersion();
52485254
auto availableAttr = availableRangeAttrPair.value().first;
52495255
auto introVersion = availableAttr.getIntroduced().value();
52505256
AvailabilityDomain introDomain = availableAttr.getDomain();
@@ -5254,7 +5260,7 @@ void AttributeChecker::checkBackDeployedAttrs(
52545260
AvailabilityInference::updateIntroducedAvailabilityDomainForFallback(
52555261
availableAttr, Ctx, introDomain, introVersion);
52565262

5257-
if (Attr->Version <= introVersion) {
5263+
if (beforeVersion <= introVersion) {
52585264
diagnose(AtLoc, diag::attr_has_no_effect_decl_not_available_before,
52595265
Attr, VD, beforeDomain, AvailabilityRange(beforeVersion));
52605266
diagnose(availableAttr.getParsedAttr()->AtLoc,

lib/Serialization/Serialization.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3164,14 +3164,14 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
31643164

31653165
case DeclAttrKind::BackDeployed: {
31663166
auto *theAttr = cast<BackDeployedAttr>(DA);
3167-
ENCODE_VER_TUPLE(Version,
3168-
std::optional<llvm::VersionTuple>(theAttr->Version));
3167+
ENCODE_VER_TUPLE(
3168+
Version, std::optional<llvm::VersionTuple>(theAttr->getVersion()));
31693169
auto abbrCode = S.DeclTypeAbbrCodes[BackDeployedDeclAttrLayout::Code];
31703170
BackDeployedDeclAttrLayout::emitRecord(
31713171
S.Out, S.ScratchRecord, abbrCode,
31723172
theAttr->isImplicit(),
31733173
LIST_VER_TUPLE_PIECES(Version),
3174-
static_cast<unsigned>(theAttr->Platform));
3174+
static_cast<unsigned>(theAttr->getPlatform()));
31753175
return;
31763176
}
31773177

test/ModuleInterface/back-deployed-attr.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,21 @@ public func backDeployTopLevelFunc_macOS() -> Int {
7272
#endif
7373
}
7474

75+
// CHECK: @backDeployed(before: macOS 26.0, iOS 26.0, tvOS 26.0, watchOS 26.0, visionOS 26.0)
76+
// CHECK: public func backDeployedBeforeVersionsMappingTo26() -> Swift.Int { return 26 }
77+
@backDeployed(before: macOS 16.0, iOS 19.0, tvOS 19.0, watchOS 12.0, visionOS 3.0)
78+
public func backDeployedBeforeVersionsMappingTo26() -> Int { return 26 }
79+
80+
// CHECK: @backDeployed(before: macOS 27.0, iOS 27.0, tvOS 27.0, watchOS 27.0, visionOS 27.0)
81+
// CHECK: public func backDeployedBeforeVersionsMappingTo27() -> Swift.Int { return 27 }
82+
@backDeployed(before: macOS 17.0, iOS 20.0, tvOS 20.0, watchOS 13.0, visionOS 4.0)
83+
public func backDeployedBeforeVersionsMappingTo27() -> Int { return 27 }
84+
85+
// CHECK: @backDeployed(before: macOS 26.0, iOS 26.0, tvOS 26.0, watchOS 26.0, visionOS 26.0)
86+
// CHECK: public func backDeployedBefore26() -> Swift.Int { return 26 }
87+
@backDeployed(before: macOS 26.0, iOS 26.0, tvOS 26.0, watchOS 26.0, visionOS 26.0)
88+
public func backDeployedBefore26() -> Int { return 26 }
89+
7590
// MARK: - Availability macros
7691

7792
// CHECK: @backDeployed(before: macOS 12.1)

test/SILGen/back_deployed_attr.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,15 @@ public func backDeployedNonisolatedNonsending() async -> Int {
100100
// CHECK: apply [[SHIPPING_FN]](%0)
101101
return 0
102102
}
103+
104+
// CHECK-LABEL: sil non_abi [serialized] [back_deployed_thunk] [ossa] @$s11back_deploy0A32DeployedBeforeVersionMappingTo26SiyFTwb :
105+
@backDeployed(before: macOS 16)
106+
public func backDeployedBeforeVersionMappingTo26() -> Int {
107+
// CHECK: [[MAJOR:%.*]] = integer_literal $Builtin.Word, 26
108+
// CHECK: [[MINOR:%.*]] = integer_literal $Builtin.Word, 0
109+
// CHECK: [[PATCH:%.*]] = integer_literal $Builtin.Word, 0
110+
// CHECK: [[OSVFN:%.*]] = function_ref @$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
111+
// CHECK: [[AVAIL:%.*]] = apply [[OSVFN]]([[MAJOR]], [[MINOR]], [[PATCH]]) : $@convention(thin) (Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1
112+
// CHECK: cond_br [[AVAIL]]
113+
return 0
114+
}

test/attr/attr_backDeployed.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,19 @@ extension TopLevelProtocol {
205205
public func backDeployedExtensionMethod() {}
206206
}
207207

208+
@backDeployed(before: macOS 16.0, iOS 19.0, tvOS 19.0, watchOS 12.0, visionOS 3.0)
209+
public func backDeployedBeforeVersionsMappingTo26() -> Int { 26 }
210+
211+
@backDeployed(before: macOS 17.0, iOS 20.0, tvOS 20.0, watchOS 13.0, visionOS 4.0)
212+
// expected-warning@-1 {{'17.0' is not a valid version number for macOS}}
213+
// expected-warning@-2 {{'20.0' is not a valid version number for iOS}}
214+
// expected-warning@-3 {{'20.0' is not a valid version number for tvOS}}
215+
// expected-warning@-4 {{'13.0' is not a valid version number for watchOS}}
216+
// expected-warning@-5 {{'4.0' is not a valid version number for visionOS}}
217+
public func backDeployedBeforeVersionsMappingTo27() -> Int { 27 }
218+
219+
@backDeployed(before: macOS 26.0, iOS 26.0, tvOS 26.0, watchOS 26.0, visionOS 26.0)
220+
public func backDeployedBefore26() -> Int { 26 }
208221

209222
// MARK: - Unsupported declaration kinds
210223

0 commit comments

Comments
 (0)