Skip to content

Commit d505527

Browse files
authored
Merge pull request #84454 from tshortli/deprecated-custom-availability-domain-fix-its
2 parents 740400e + 8a1338d commit d505527

File tree

12 files changed

+329
-10
lines changed

12 files changed

+329
-10
lines changed

include/swift/AST/AvailabilityDomain.h

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,12 @@ class AvailabilityDomain final {
289289
return getRemappedDomain(ctx, unused);
290290
}
291291

292+
/// Returns true for a domain that is permanently always available, and
293+
/// therefore availability constraints in the domain are effectively the same
294+
/// as constraints in the `*` domain. This is used to diagnose unnecessary
295+
/// `@available` attributes and `if #available` statements.
296+
bool isPermanentlyAlwaysEnabled() const;
297+
292298
bool operator==(const AvailabilityDomain &other) const {
293299
return storage.getOpaqueValue() == other.storage.getOpaqueValue();
294300
}
@@ -330,7 +336,7 @@ struct StableAvailabilityDomainComparator {
330336
/// Represents an availability domain that has been defined in a module.
331337
class CustomAvailabilityDomain : public llvm::FoldingSetNode {
332338
public:
333-
enum class Kind {
339+
enum class Kind : uint8_t {
334340
/// A domain that is known to be enabled at compile time.
335341
Enabled,
336342
/// A domain that is known to be enabled at compile time and is also assumed
@@ -344,10 +350,20 @@ class CustomAvailabilityDomain : public llvm::FoldingSetNode {
344350

345351
private:
346352
Identifier name;
347-
Kind kind;
348353
ModuleDecl *mod;
349354
ValueDecl *decl;
350355
FuncDecl *predicateFunc;
356+
Kind kind;
357+
358+
struct {
359+
/// Whether the "isPermanentlyEnabled" bit has been computed yet.
360+
unsigned isPermanentlyEnabledComputed : 1;
361+
/// Whether the domain is permanently enabled, which makes constraints in
362+
/// the domain equivalent to those in the `*` domain.
363+
unsigned isPermanentlyEnabled : 1;
364+
} flags = {};
365+
366+
friend class IsCustomAvailabilityDomainPermanentlyEnabled;
351367

352368
CustomAvailabilityDomain(Identifier name, Kind kind, ModuleDecl *mod,
353369
ValueDecl *decl, FuncDecl *predicateFunc);
@@ -375,6 +391,11 @@ class CustomAvailabilityDomain : public llvm::FoldingSetNode {
375391
void Profile(llvm::FoldingSetNodeID &ID) const { Profile(ID, name, mod); }
376392
};
377393

394+
inline void simple_display(llvm::raw_ostream &os,
395+
const CustomAvailabilityDomain *domain) {
396+
os << domain->getName();
397+
}
398+
378399
/// Represents either a resolved availability domain or an identifier written
379400
/// in source that has not yet been resolved to a domain.
380401
class AvailabilityDomainOrIdentifier {

include/swift/AST/DiagnosticGroups.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
GROUP(no_group, "")
4242

4343
GROUP(ActorIsolatedCall, "actor-isolated-call")
44+
GROUP(AlwaysAvailableDomain, "always-available-domain")
4445
GROUP(AvailabilityUnrecognizedName, "availability-unrecognized-name")
4546
GROUP(ClangDeclarationImport, "clang-declaration-import")
4647
GROUP(ConformanceIsolation, "conformance-isolation")

include/swift/AST/DiagnosticsSema.def

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7013,6 +7013,15 @@ ERROR(attr_availability_domain_not_usable_from_inline, none,
70137013
"'@usableFromInline' or public",
70147014
(AvailabilityDomain, DeclAttribute, const Decl *))
70157015

7016+
GROUPED_WARNING(attr_availability_has_no_effect_domain_always_available,
7017+
AlwaysAvailableDomain, none,
7018+
"'%0' has no effect because '%1' is always available",
7019+
(DeclAttribute, AvailabilityDomain))
7020+
GROUPED_WARNING(attr_availability_domain_always_available,
7021+
AlwaysAvailableDomain, none,
7022+
"'%0' is always available, use '*' instead",
7023+
(AvailabilityDomain))
7024+
70167025
ERROR(availability_decl_unavailable, none,
70177026
"%0 is unavailable%select{ in %2|}1%select{|: %3}3",
70187027
(const ValueDecl *, bool, AvailabilityDomain, StringRef))
@@ -7134,6 +7143,12 @@ WARNING(availability_query_useless_enclosing_scope, none,
71347143
NOTE(availability_query_useless_enclosing_scope_here, none,
71357144
"enclosing scope here", ())
71367145

7146+
GROUPED_WARNING(availability_query_useless_always_true,
7147+
AlwaysAvailableDomain, none,
7148+
"unnecessary check for '%0'; "
7149+
"this condition will always be %select{false|true}1",
7150+
(AvailabilityDomain, unsigned))
7151+
71377152
ERROR(availability_decl_no_potential, none,
71387153
"%kindonly0 cannot be marked potentially unavailable with '@available'",
71397154
(const Decl *))

include/swift/AST/TypeCheckRequests.h

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5505,7 +5505,6 @@ class AvailabilityDomainForDeclRequest
55055505
private:
55065506
friend SimpleRequest;
55075507

5508-
// Evaluation.
55095508
std::optional<AvailabilityDomain> evaluate(Evaluator &evaluator,
55105509
ValueDecl *decl) const;
55115510

@@ -5515,6 +5514,30 @@ class AvailabilityDomainForDeclRequest
55155514
void cacheResult(std::optional<AvailabilityDomain> domain) const;
55165515
};
55175516

5517+
class IsCustomAvailabilityDomainPermanentlyEnabled
5518+
: public SimpleRequest<IsCustomAvailabilityDomainPermanentlyEnabled,
5519+
bool(const CustomAvailabilityDomain *),
5520+
RequestFlags::SeparatelyCached> {
5521+
public:
5522+
using SimpleRequest::SimpleRequest;
5523+
5524+
private:
5525+
friend SimpleRequest;
5526+
5527+
bool evaluate(Evaluator &evaluator,
5528+
const CustomAvailabilityDomain *customDomain) const;
5529+
5530+
public:
5531+
bool isCached() const { return true; }
5532+
std::optional<bool> getCachedResult() const;
5533+
void cacheResult(bool isPermanentlyEnabled) const;
5534+
5535+
SourceLoc getNearestLoc() const {
5536+
auto *domain = std::get<0>(getStorage());
5537+
return extractNearestSourceLoc(domain->getDecl());
5538+
}
5539+
};
5540+
55185541
#define SWIFT_TYPEID_ZONE TypeChecker
55195542
#define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def"
55205543
#include "swift/Basic/DefineTypeIDZone.h"

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,3 +660,7 @@ SWIFT_REQUEST(TypeChecker, ModuleHasTypeCheckerPerformanceHacksEnabledRequest,
660660
SWIFT_REQUEST(TypeChecker, AvailabilityDomainForDeclRequest,
661661
std::optional<AvailabilityDomain>(ValueDecl *),
662662
Cached | SplitCached, NoLocationInfo)
663+
664+
SWIFT_REQUEST(TypeChecker, IsCustomAvailabilityDomainPermanentlyEnabled,
665+
bool(const CustomAvailabilityDomain *),
666+
SeparatelyCached, NoLocationInfo)

lib/AST/AvailabilityDomain.cpp

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,45 @@ AvailabilityDomain::getRemappedDomain(const ASTContext &ctx,
357357
return *this;
358358
}
359359

360+
bool IsCustomAvailabilityDomainPermanentlyEnabled::evaluate(
361+
Evaluator &evaluator, const CustomAvailabilityDomain *customDomain) const {
362+
switch (customDomain->getKind()) {
363+
case CustomAvailabilityDomain::Kind::Enabled:
364+
case CustomAvailabilityDomain::Kind::Disabled:
365+
case CustomAvailabilityDomain::Kind::Dynamic:
366+
return false;
367+
368+
case CustomAvailabilityDomain::Kind::AlwaysEnabled:
369+
break;
370+
}
371+
372+
auto *domainDecl = customDomain->getDecl();
373+
if (!domainDecl)
374+
return false;
375+
376+
if (auto deprecatedAttr = domainDecl->getDeprecatedAttr()) {
377+
if (deprecatedAttr->getDomain().isUniversal())
378+
return true;
379+
}
380+
381+
if (auto unavailableAttr = domainDecl->getUnavailableAttr()) {
382+
if (unavailableAttr->getDomain().isUniversal())
383+
return true;
384+
}
385+
386+
return false;
387+
}
388+
389+
bool AvailabilityDomain::isPermanentlyAlwaysEnabled() const {
390+
if (auto *customDomain = getCustomDomain()) {
391+
if (auto *domainDecl = customDomain->getDecl())
392+
return evaluateOrDefault(
393+
domainDecl->getASTContext().evaluator,
394+
IsCustomAvailabilityDomainPermanentlyEnabled{customDomain}, false);
395+
}
396+
return false;
397+
}
398+
360399
void AvailabilityDomain::print(llvm::raw_ostream &os) const {
361400
os << getNameForAttributePrinting();
362401
}
@@ -408,8 +447,8 @@ CustomAvailabilityDomain::CustomAvailabilityDomain(Identifier name, Kind kind,
408447
ModuleDecl *mod,
409448
ValueDecl *decl,
410449
FuncDecl *predicateFunc)
411-
: name(name), kind(kind), mod(mod), decl(decl),
412-
predicateFunc(predicateFunc) {
450+
: name(name), mod(mod), decl(decl), predicateFunc(predicateFunc),
451+
kind(kind) {
413452
ASSERT(!name.empty());
414453
ASSERT(mod);
415454
if (predicateFunc)

lib/AST/TypeCheckRequests.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2867,3 +2867,23 @@ void AvailabilityDomainForDeclRequest::cacheResult(
28672867

28682868
decl->getASTContext().evaluator.cacheNonEmptyOutput(*this, std::move(domain));
28692869
}
2870+
2871+
//----------------------------------------------------------------------------//
2872+
// IsCustomAvailabilityDomainPermanentlyEnabled computation.
2873+
//----------------------------------------------------------------------------//
2874+
std::optional<bool>
2875+
IsCustomAvailabilityDomainPermanentlyEnabled::getCachedResult() const {
2876+
auto *domain = std::get<0>(getStorage());
2877+
2878+
if (domain->flags.isPermanentlyEnabledComputed)
2879+
return domain->flags.isPermanentlyEnabled;
2880+
return std::nullopt;
2881+
}
2882+
2883+
void IsCustomAvailabilityDomainPermanentlyEnabled::cacheResult(
2884+
bool isPermanentlyEnabled) const {
2885+
auto *domain = const_cast<CustomAvailabilityDomain *>(std::get<0>(getStorage()));
2886+
2887+
domain->flags.isPermanentlyEnabledComputed = true;
2888+
domain->flags.isPermanentlyEnabled = isPermanentlyEnabled;
2889+
}

lib/Sema/TypeCheckAccess.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2281,12 +2281,45 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
22812281
void checkAvailabilityDomains(const Decl *D) {
22822282
D = D->getAbstractSyntaxDeclForAttributes();
22832283

2284+
auto &ctx = D->getASTContext();
22842285
auto where = Where.withReason(ExportabilityReason::AvailableAttribute);
22852286
for (auto attr : D->getSemanticAvailableAttrs()) {
2286-
if (auto *domainDecl = attr.getDomain().getDecl()) {
2287+
auto domain = attr.getDomain();
2288+
if (auto *domainDecl = domain.getDecl()) {
22872289
diagnoseDeclAvailability(domainDecl,
22882290
attr.getParsedAttr()->getDomainLoc(), nullptr,
22892291
where, std::nullopt);
2292+
2293+
if (!attr.isVersionSpecific()) {
2294+
// Check whether the availability domain is permanently enabled. If it
2295+
// is, suggest modifying or removing the attribute.
2296+
if (domain.isPermanentlyAlwaysEnabled()) {
2297+
auto parsedAttr = attr.getParsedAttr();
2298+
switch (parsedAttr->getKind()) {
2299+
case AvailableAttr::Kind::Default:
2300+
// This introduction constraint has no effect since it will never
2301+
// restrict use of the declaration. Provide a fix-it remove the
2302+
// attribute.
2303+
diagnoseAndRemoveAttr(
2304+
D, attr.getParsedAttr(),
2305+
diag::attr_availability_has_no_effect_domain_always_available,
2306+
parsedAttr, domain);
2307+
break;
2308+
case AvailableAttr::Kind::Deprecated:
2309+
case AvailableAttr::Kind::NoAsync:
2310+
case AvailableAttr::Kind::Unavailable:
2311+
// Any other kind of constraint always constrains use of the
2312+
// declaration, so provide a fix-it to specify `*` instead of the
2313+
// custom domain.
2314+
ctx.Diags
2315+
.diagnose(parsedAttr->getDomainLoc(),
2316+
diag::attr_availability_domain_always_available,
2317+
domain)
2318+
.fixItReplace(parsedAttr->getDomainLoc(), "*");
2319+
break;
2320+
}
2321+
}
2322+
}
22902323
}
22912324
}
22922325
}

lib/Sema/TypeCheckStmt.cpp

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,37 @@ ConcreteDeclRef TypeChecker::getReferencedDeclForHasSymbolCondition(Expr *E) {
810810
return ConcreteDeclRef();
811811
}
812812

813+
static bool typeCheckAvailableStmtConditionElement(StmtConditionElement &elt,
814+
bool &isFalseable,
815+
DeclContext *dc) {
816+
auto info = elt.getAvailability();
817+
if (info->isInvalid()) {
818+
isFalseable = true;
819+
return false;
820+
}
821+
822+
auto &diags = dc->getASTContext().Diags;
823+
bool isConditionAlwaysTrue = false;
824+
825+
if (auto query = info->getAvailabilityQuery()) {
826+
auto domain = query->getDomain();
827+
if (query->isConstant() && domain.isPermanentlyAlwaysEnabled()) {
828+
isConditionAlwaysTrue = *query->getConstantResult();
829+
830+
diags
831+
.diagnose(elt.getStartLoc(),
832+
diag::availability_query_useless_always_true, domain,
833+
isConditionAlwaysTrue)
834+
.highlight(elt.getSourceRange());
835+
}
836+
}
837+
838+
if (!isConditionAlwaysTrue)
839+
isFalseable = true;
840+
841+
return false;
842+
}
843+
813844
static bool typeCheckHasSymbolStmtConditionElement(StmtConditionElement &elt,
814845
DeclContext *dc) {
815846
auto Info = elt.getHasSymbolInfo();
@@ -895,8 +926,7 @@ bool TypeChecker::typeCheckStmtConditionElement(StmtConditionElement &elt,
895926
DeclContext *dc) {
896927
switch (elt.getKind()) {
897928
case StmtConditionElement::CK_Availability:
898-
isFalsable = true;
899-
return false;
929+
return typeCheckAvailableStmtConditionElement(elt, isFalsable, dc);
900930
case StmtConditionElement::CK_HasSymbol:
901931
isFalsable = true;
902932
return typeCheckHasSymbolStmtConditionElement(elt, dc);
@@ -924,8 +954,9 @@ static bool typeCheckConditionForStatement(LabeledConditionalStmt *stmt,
924954
TypeChecker::typeCheckStmtConditionElement(elt, hadAnyFalsable, dc);
925955
}
926956

927-
// If the binding is not refutable, and there *is* an else, reject it as
928-
// unreachable.
957+
// If none of the statement's conditions can be false, diagnose.
958+
// FIXME: Also diagnose if none of the statements conditions can be true.
959+
// FIXME: Offer a fix-it to remove the unreachable code.
929960
if (!hadAnyFalsable && !hadError) {
930961
auto &diags = dc->getASTContext().Diags;
931962
Diag<> msg = diag::invalid_diagnostic;

0 commit comments

Comments
 (0)