Skip to content

Commit 1436c75

Browse files
committed
Sema: Lift restriction that declarations with @_backDeploy must have explicit availability.
Resolves rdar://103880356
1 parent b9609a6 commit 1436c75

13 files changed

+271
-76
lines changed

include/swift/AST/Attr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2367,6 +2367,10 @@ class DeclAttributes {
23672367
/// otherwise.
23682368
const AvailableAttr *getNoAsync(const ASTContext &ctx) const;
23692369

2370+
/// Returns the \c @_backDeploy attribute that is active for the current
2371+
/// platform.
2372+
const BackDeployAttr *getBackDeploy(const ASTContext &ctx) const;
2373+
23702374
SWIFT_DEBUG_DUMPER(dump(const Decl *D = nullptr));
23712375
void print(ASTPrinter &Printer, const PrintOptions &Options,
23722376
const Decl *D = nullptr) const;

include/swift/AST/Availability.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
namespace swift {
2626
class ASTContext;
27+
class AvailableAttr;
2728
class Decl;
2829

2930
/// A lattice of version ranges of the form [x.y.z, +Inf).
@@ -344,6 +345,11 @@ class AvailabilityInference {
344345
/// We assume a declaration without an annotation is always available.
345346
static AvailabilityContext availableRange(const Decl *D, ASTContext &C);
346347

348+
/// Returns the attribute that should be used to determine the availability
349+
/// range of the given declaration, or nullptr if there is none.
350+
static const AvailableAttr *attrForAnnotatedAvailableRange(const Decl *D,
351+
ASTContext &Ctx);
352+
347353
/// Returns the context for which the declaration
348354
/// is annotated as available, or None if the declaration
349355
/// has no availability annotation.

include/swift/AST/Decl.h

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,11 +1098,22 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
10981098

10991099
bool isAvailableAsSPI() const;
11001100

1101-
/// Whether the declaration is considered unavailable through either being
1102-
/// explicitly marked as such, or has a parent decl that is semantically
1103-
/// unavailable. This is a broader notion of unavailability than is checked by
1104-
/// \c AvailableAttr::isUnavailable.
1105-
bool isSemanticallyUnavailable() const;
1101+
/// Retrieve the @available attribute that provides the OS version range that
1102+
/// this declaration is available in.
1103+
///
1104+
/// The attribute may come from another declaration, since availability
1105+
/// could be inherited from a parent declaration.
1106+
Optional<std::pair<const AvailableAttr *, const Decl *>>
1107+
getSemanticAvailableRangeAttr() const;
1108+
1109+
/// Retrieve the @available attribute that makes this declaration unavailable,
1110+
/// if any.
1111+
///
1112+
/// The attribute may come from another declaration, since unavailability
1113+
/// could be inherited from a parent declaration. This is a broader notion of
1114+
/// unavailability than is checked by \c AvailableAttr::isUnavailable.
1115+
Optional<std::pair<const AvailableAttr *, const Decl *>>
1116+
getSemanticUnavailableAttr() const;
11061117

11071118
// List the SPI groups declared with @_spi or inherited by this decl.
11081119
//

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3290,8 +3290,14 @@ ERROR(attr_not_on_decl_with_invalid_access_level,none,
32903290
(DeclAttribute, AccessLevel))
32913291

32923292
ERROR(attr_has_no_effect_decl_not_available_before,none,
3293-
"'%0' has no effect because %1 is not available before %2 %3",
3294-
(DeclAttribute, DeclName, StringRef, llvm::VersionTuple))
3293+
"'%0' has no effect because %select{getter for |setter for |}1%2 is not "
3294+
"available before %3 %4",
3295+
(DeclAttribute, unsigned, DeclName, StringRef, llvm::VersionTuple))
3296+
3297+
ERROR(attr_has_no_effect_on_unavailable_decl,none,
3298+
"'%0' has no effect because %select{getter for |setter for |}1%2 is "
3299+
"unavailable on %3",
3300+
(DeclAttribute, unsigned, DeclName, StringRef))
32953301

32963302
ERROR(attr_ambiguous_reference_to_decl,none,
32973303
"ambiguous reference to %0 in '@%1' attribute", (DeclNameRef, StringRef))

include/swift/AST/TypeCheckRequests.h

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3480,17 +3480,37 @@ class RenamedDeclRequest
34803480
bool isCached() const { return true; }
34813481
};
34823482

3483-
class IsSemanticallyUnavailableRequest
3484-
: public SimpleRequest<IsSemanticallyUnavailableRequest,
3485-
bool(const Decl *),
3483+
using AvailableAttrDeclPair = std::pair<const AvailableAttr *, const Decl *>;
3484+
3485+
class SemanticAvailableRangeAttrRequest
3486+
: public SimpleRequest<SemanticAvailableRangeAttrRequest,
3487+
Optional<AvailableAttrDeclPair>(const Decl *),
3488+
RequestFlags::Cached> {
3489+
public:
3490+
using SimpleRequest::SimpleRequest;
3491+
3492+
private:
3493+
friend SimpleRequest;
3494+
3495+
Optional<AvailableAttrDeclPair> evaluate(Evaluator &evaluator,
3496+
const Decl *decl) const;
3497+
3498+
public:
3499+
bool isCached() const { return true; }
3500+
};
3501+
3502+
class SemanticUnavailableAttrRequest
3503+
: public SimpleRequest<SemanticUnavailableAttrRequest,
3504+
Optional<AvailableAttrDeclPair>(const Decl *),
34863505
RequestFlags::Cached> {
34873506
public:
34883507
using SimpleRequest::SimpleRequest;
34893508

34903509
private:
34913510
friend SimpleRequest;
34923511

3493-
bool evaluate(Evaluator &evaluator, const Decl *decl) const;
3512+
Optional<AvailableAttrDeclPair> evaluate(Evaluator &evaluator,
3513+
const Decl *decl) const;
34943514

34953515
public:
34963516
bool isCached() const { return true; }

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,8 +392,11 @@ SWIFT_REQUEST(TypeChecker, GetImplicitSendableRequest,
392392
SWIFT_REQUEST(TypeChecker, RenamedDeclRequest,
393393
ValueDecl *(const ValueDecl *, const AvailableAttr *),
394394
Cached, NoLocationInfo)
395-
SWIFT_REQUEST(TypeChecker, IsSemanticallyUnavailableRequest,
396-
bool(const Decl *),
395+
SWIFT_REQUEST(TypeChecker, SemanticAvailableRangeAttrRequest,
396+
Optional<AvailableAttrDeclPair>(const Decl *),
397+
Cached, NoLocationInfo)
398+
SWIFT_REQUEST(TypeChecker, SemanticUnavailableAttrRequest,
399+
Optional<AvailableAttrDeclPair>(const Decl *),
397400
Cached, NoLocationInfo)
398401
SWIFT_REQUEST(TypeChecker, ClosureEffectsRequest,
399402
FunctionType::ExtInfo(ClosureExpr *),

lib/AST/Attr.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,29 @@ const AvailableAttr *DeclAttributes::getNoAsync(const ASTContext &ctx) const {
398398
return bestAttr;
399399
}
400400

401+
const BackDeployAttr *
402+
DeclAttributes::getBackDeploy(const ASTContext &ctx) const {
403+
const BackDeployAttr *bestAttr = nullptr;
404+
405+
for (auto attr : *this) {
406+
auto *backDeployAttr = dyn_cast<BackDeployAttr>(attr);
407+
if (!backDeployAttr)
408+
continue;
409+
410+
if (backDeployAttr->isInvalid() || !backDeployAttr->isActivePlatform(ctx))
411+
continue;
412+
413+
// We have an attribute that is active for the platform, but
414+
// is it more specific than our current best?
415+
if (!bestAttr || inheritsAvailabilityFromPlatform(backDeployAttr->Platform,
416+
bestAttr->Platform)) {
417+
bestAttr = backDeployAttr;
418+
}
419+
}
420+
421+
return bestAttr;
422+
}
423+
401424
void DeclAttributes::dump(const Decl *D) const {
402425
StreamPrinter P(llvm::errs());
403426
PrintOptions PO = PrintOptions::printDeclarations();

lib/AST/Availability.cpp

Lines changed: 56 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,25 @@ void AvailabilityInference::applyInferredAvailableAttrs(
145145
}
146146
}
147147

148+
/// Returns the decl that should be considered the parent decl of the given decl
149+
/// when looking for inherited availability annotations.
150+
static Decl *parentDeclForAvailability(const Decl *D) {
151+
if (auto *AD = dyn_cast<AccessorDecl>(D))
152+
return AD->getStorage();
153+
154+
if (auto *ED = dyn_cast<ExtensionDecl>(D)) {
155+
if (auto *NTD = ED->getExtendedNominal())
156+
return NTD;
157+
}
158+
159+
// Clang decls may be inaccurately parented rdar://53956555
160+
if (D->hasClangNode())
161+
return nullptr;
162+
163+
// Availability is inherited from the enclosing context.
164+
return D->getDeclContext()->getInnermostDeclarationDeclContext();
165+
}
166+
148167
/// Returns true if the introduced version in \p newAttr should be used instead
149168
/// of the introduced version in \p prevAttr when both are attached to the same
150169
/// declaration and refer to the active platform.
@@ -165,8 +184,9 @@ static bool isBetterThan(const AvailableAttr *newAttr,
165184
prevAttr->Platform);
166185
}
167186

168-
Optional<AvailabilityContext>
169-
AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) {
187+
const AvailableAttr *
188+
AvailabilityInference::attrForAnnotatedAvailableRange(const Decl *D,
189+
ASTContext &Ctx) {
170190
const AvailableAttr *bestAvailAttr = nullptr;
171191

172192
for (auto Attr : D->getAttrs()) {
@@ -182,6 +202,30 @@ AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) {
182202
bestAvailAttr = AvailAttr;
183203
}
184204

205+
return bestAvailAttr;
206+
}
207+
208+
Optional<AvailableAttrDeclPair>
209+
SemanticAvailableRangeAttrRequest::evaluate(Evaluator &evaluator,
210+
const Decl *decl) const {
211+
if (auto attr = AvailabilityInference::attrForAnnotatedAvailableRange(
212+
decl, decl->getASTContext()))
213+
return std::make_pair(attr, decl);
214+
215+
if (auto *parent = parentDeclForAvailability(decl))
216+
return parent->getSemanticAvailableRangeAttr();
217+
218+
return None;
219+
}
220+
221+
Optional<AvailableAttrDeclPair> Decl::getSemanticAvailableRangeAttr() const {
222+
auto &eval = getASTContext().evaluator;
223+
return evaluateOrDefault(eval, SemanticAvailableRangeAttrRequest{this}, None);
224+
}
225+
226+
Optional<AvailabilityContext>
227+
AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) {
228+
auto bestAvailAttr = attrForAnnotatedAvailableRange(D, Ctx);
185229
if (!bestAvailAttr)
186230
return None;
187231

@@ -195,39 +239,22 @@ bool Decl::isAvailableAsSPI() const {
195239
.isAvailableAsSPI();
196240
}
197241

198-
bool IsSemanticallyUnavailableRequest::evaluate(Evaluator &evaluator,
199-
const Decl *decl) const {
242+
Optional<AvailableAttrDeclPair>
243+
SemanticUnavailableAttrRequest::evaluate(Evaluator &evaluator,
244+
const Decl *decl) const {
200245
// Directly marked unavailable.
201-
if (AvailableAttr::isUnavailable(decl))
202-
return true;
246+
if (auto attr = decl->getAttrs().getUnavailable(decl->getASTContext()))
247+
return std::make_pair(attr, decl);
203248

204-
// If this is an extension, it's semantically unavailable if its nominal is,
205-
// as there is no way to reference or construct the type.
206-
if (auto *ext = dyn_cast<ExtensionDecl>(decl)) {
207-
if (auto *nom = ext->getExtendedNominal()) {
208-
if (nom->isSemanticallyUnavailable())
209-
return true;
210-
}
211-
}
249+
if (auto *parent = parentDeclForAvailability(decl))
250+
return parent->getSemanticUnavailableAttr();
212251

213-
// If the parent decl is semantically unavailable, then this decl is too.
214-
// For local contexts, this means it's a local decl in e.g an unavailable
215-
// function, which cannot be accessed. For non-local contexts, this is a
216-
// nested type or a member with an unavailable parent, which cannot be
217-
// referenced.
218-
// Similar to `AvailableAttr::isUnavailable`, don't apply this logic to
219-
// Clang decls, as they may be inaccurately parented.
220-
if (!decl->hasClangNode()) {
221-
auto *DC = decl->getDeclContext();
222-
if (auto *parentDecl = DC->getInnermostDeclarationDeclContext())
223-
return parentDecl->isSemanticallyUnavailable();
224-
}
225-
return false;
252+
return None;
226253
}
227254

228-
bool Decl::isSemanticallyUnavailable() const {
255+
Optional<AvailableAttrDeclPair> Decl::getSemanticUnavailableAttr() const {
229256
auto &eval = getASTContext().evaluator;
230-
return evaluateOrDefault(eval, IsSemanticallyUnavailableRequest{this}, false);
257+
return evaluateOrDefault(eval, SemanticUnavailableAttrRequest{this}, None);
231258
}
232259

233260
bool UnavailabilityReason::requiresDeploymentTargetOrEarlier(

lib/AST/Decl.cpp

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -390,19 +390,12 @@ Decl::getIntroducedOSVersion(PlatformKind Kind) const {
390390

391391
Optional<llvm::VersionTuple>
392392
Decl::getBackDeployBeforeOSVersion(ASTContext &Ctx) const {
393-
for (auto *attr : getAttrs()) {
394-
if (auto *backDeployAttr = dyn_cast<BackDeployAttr>(attr)) {
395-
if (backDeployAttr->isActivePlatform(Ctx) && backDeployAttr->Version) {
396-
return backDeployAttr->Version;
397-
}
398-
}
399-
}
393+
if (auto *attr = getAttrs().getBackDeploy(Ctx))
394+
return attr->Version;
400395

401396
// Accessors may inherit `@_backDeploy`.
402-
if (getKind() == DeclKind::Accessor) {
403-
return cast<AccessorDecl>(this)->getStorage()->getBackDeployBeforeOSVersion(
404-
Ctx);
405-
}
397+
if (auto *AD = dyn_cast<AccessorDecl>(this))
398+
return AD->getStorage()->getBackDeployBeforeOSVersion(Ctx);
406399

407400
return None;
408401
}

lib/SIL/IR/SILProfiler.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ static bool shouldProfile(SILDeclRef Constant) {
9999
// Do not profile AST nodes in unavailable contexts.
100100
auto *DC = Constant.getInnermostDeclContext();
101101
if (auto *D = DC->getInnermostDeclarationDeclContext()) {
102-
if (D->isSemanticallyUnavailable()) {
102+
if (D->getSemanticUnavailableAttr()) {
103103
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: unavailable context\n");
104104
return false;
105105
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4520,6 +4520,8 @@ void AttributeChecker::checkBackDeployAttrs(ArrayRef<BackDeployAttr *> Attrs) {
45204520
auto *VD = cast<ValueDecl>(D);
45214521
std::map<PlatformKind, SourceLoc> seenPlatforms;
45224522

4523+
auto *ActiveAttr = D->getAttrs().getBackDeploy(Ctx);
4524+
45234525
for (auto *Attr : Attrs) {
45244526
// Back deployment only makes sense for public declarations.
45254527
if (diagnoseAndRemoveAttrIfDeclIsNonPublic(Attr, /*isError=*/true))
@@ -4559,18 +4561,47 @@ void AttributeChecker::checkBackDeployAttrs(ArrayRef<BackDeployAttr *> Attrs) {
45594561
continue;
45604562
}
45614563

4562-
// Require explicit availability for back deployed decls.
4563-
if (diagnoseMissingAvailability(Attr, Platform))
4564+
if (Ctx.LangOpts.DisableAvailabilityChecking)
4565+
continue;
4566+
4567+
// Availability conflicts can only be diagnosed for attributes that apply
4568+
// to the active platform.
4569+
if (Attr != ActiveAttr)
45644570
continue;
45654571

4572+
// Unavailable decls cannot be back deployed.
4573+
if (auto unavailableAttrPair = VD->getSemanticUnavailableAttr()) {
4574+
auto unavailableAttr = unavailableAttrPair.value().first;
4575+
DeclName name;
4576+
unsigned accessorKind;
4577+
std::tie(accessorKind, name) = getAccessorKindAndNameForDiagnostics(VD);
4578+
diagnose(AtLoc, diag::attr_has_no_effect_on_unavailable_decl, Attr,
4579+
accessorKind, name, prettyPlatformString(Platform));
4580+
diagnose(unavailableAttr->AtLoc, diag::availability_marked_unavailable,
4581+
accessorKind, name)
4582+
.highlight(unavailableAttr->getRange());
4583+
continue;
4584+
}
4585+
45664586
// Verify that the decl is available before the back deployment boundary.
45674587
// If it's not, the attribute doesn't make sense since the back deployment
45684588
// fallback could never be executed at runtime.
4569-
auto IntroVer = D->getIntroducedOSVersion(Platform);
4570-
if (Attr->Version <= IntroVer.value()) {
4571-
diagnose(AtLoc, diag::attr_has_no_effect_decl_not_available_before, Attr,
4572-
VD->getName(), prettyPlatformString(Platform), Attr->Version);
4573-
continue;
4589+
if (auto availableRangeAttrPair = VD->getSemanticAvailableRangeAttr()) {
4590+
auto availableAttr = availableRangeAttrPair.value().first;
4591+
if (Attr->Version <= availableAttr->Introduced.value()) {
4592+
DeclName name;
4593+
unsigned accessorKind;
4594+
std::tie(accessorKind, name) = getAccessorKindAndNameForDiagnostics(VD);
4595+
diagnose(AtLoc, diag::attr_has_no_effect_decl_not_available_before,
4596+
Attr, accessorKind, name, prettyPlatformString(Platform),
4597+
Attr->Version);
4598+
diagnose(availableAttr->AtLoc, diag::availability_introduced_in_version,
4599+
accessorKind, name,
4600+
prettyPlatformString(availableAttr->Platform),
4601+
*availableAttr->Introduced)
4602+
.highlight(availableAttr->getRange());
4603+
continue;
4604+
}
45744605
}
45754606
}
45764607
}

0 commit comments

Comments
 (0)