Skip to content

Sema: Replace UnavailabilityDiagnosticInfo with AvailabilityConstraint #78775

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/AST/AvailabilityConstraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#ifndef SWIFT_AST_AVAILABILITY_CONSTRAINT_H
#define SWIFT_AST_AVAILABILITY_CONSTRAINT_H

#include "swift/AST/AvailabilityDomain.h"
#include "swift/AST/AvailabilityRange.h"
#include "swift/AST/PlatformKind.h"
#include "swift/Basic/LLVM.h"
Expand Down Expand Up @@ -88,6 +89,9 @@ class AvailabilityConstraint {
return static_cast<SemanticAvailableAttr>(attrAndKind.getPointer());
}

/// Returns the domain that the constraint applies to.
AvailabilityDomain getDomain() const { return getAttr().getDomain(); }

/// Returns the platform that this constraint applies to, or
/// `PlatformKind::none` if it is not platform specific.
PlatformKind getPlatform() const;
Expand Down
224 changes: 86 additions & 138 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,19 @@ concreteSyntaxDeclForAvailableAttribute(const Decl *AbstractSyntaxDecl);
/// Emit a diagnostic for references to declarations that have been
/// marked as unavailable, either through "unavailable" or "obsoleted:".
static bool diagnoseExplicitUnavailability(
SourceLoc loc, const RootProtocolConformance *rootConf,
const ExtensionDecl *ext, const ExportContext &where,
SourceLoc loc, const AvailabilityConstraint &constraint,
const RootProtocolConformance *rootConf, const ExtensionDecl *ext,
const ExportContext &where,
bool warnIfConformanceUnavailablePreSwift6 = false,
bool preconcurrency = false);

/// Emit a diagnostic for references to declarations that have been
/// marked as unavailable, either through "unavailable" or "obsoleted:".
static bool diagnoseExplicitUnavailability(
const ValueDecl *D, SourceRange R, const ExportContext &Where,
DeclAvailabilityFlags Flags,
llvm::function_ref<void(InFlightDiagnostic &)> attachRenameFixIts);
const ValueDecl *D, SourceRange R, const AvailabilityConstraint &constraint,
const ExportContext &Where, DeclAvailabilityFlags Flags,
llvm::function_ref<void(InFlightDiagnostic &, StringRef)>
attachRenameFixIts);

static bool diagnoseSubstitutionMapAvailability(
SourceLoc loc, SubstitutionMap subs, const ExportContext &where,
Expand Down Expand Up @@ -2950,11 +2952,19 @@ void swift::diagnoseOverrideOfUnavailableDecl(ValueDecl *override,
return;
}

// FIXME: [availability] Take an unsatisfied constraint as input instead of
// recomputing it.
ExportContext where = ExportContext::forDeclSignature(override, nullptr);
auto constraint =
getUnsatisfiedAvailabilityConstraint(base, where.getAvailability());
if (!constraint)
return;

diagnoseExplicitUnavailability(
base, override->getLoc(), where,
/*Flags*/ std::nullopt, [&](InFlightDiagnostic &diag) {
ParsedDeclName parsedName = parseDeclName(attr.getRename());
base, override->getLoc(), *constraint, where,
/*Flags*/ std::nullopt,
[&override, &ctx](InFlightDiagnostic &diag, StringRef rename) {
ParsedDeclName parsedName = parseDeclName(rename);
if (!parsedName || parsedName.isPropertyAccessor() ||
parsedName.isMember() || parsedName.isOperator()) {
return;
Expand All @@ -2981,127 +2991,63 @@ void swift::diagnoseOverrideOfUnavailableDecl(ValueDecl *override,

/// Emit a diagnostic for references to declarations that have been
/// marked as unavailable, either through "unavailable" or "obsoleted:".
static bool diagnoseExplicitUnavailability(const ValueDecl *D, SourceRange R,
const ExportContext &Where,
const Expr *call,
DeclAvailabilityFlags Flags) {
static bool diagnoseExplicitUnavailability(
const ValueDecl *D, SourceRange R, const AvailabilityConstraint &constraint,
const ExportContext &Where, const Expr *call, DeclAvailabilityFlags Flags) {
return diagnoseExplicitUnavailability(
D, R, Where, Flags, [=](InFlightDiagnostic &diag) {
auto attr = D->getUnavailableAttr();
assert(attr);
fixItAvailableAttrRename(diag, R, D, attr->getRename(), call);
D, R, constraint, Where, Flags,
[=](InFlightDiagnostic &diag, StringRef rename) {
fixItAvailableAttrRename(diag, R, D, rename, call);
});
}

/// Represents common information needed to emit diagnostics about explicitly
/// unavailable declarations.
class UnavailabilityDiagnosticInfo {
public:
enum class Status {
/// The declaration is marked `unavailable`, potentially on a specific
/// platform.
AlwaysUnavailable,

/// The declaration is not available until, for example, a later Swift
/// language mode.
IntroducedInVersion,

/// The declaration was obsoleted in a previous version.
Obsoleted,
};

private:
Status DiagnosticStatus;
SemanticAvailableAttr Attr;

public:
UnavailabilityDiagnosticInfo(Status status, const SemanticAvailableAttr &attr)
: DiagnosticStatus(status), Attr(attr) {};

Status getStatus() const { return DiagnosticStatus; }
SemanticAvailableAttr getAttr() const { return Attr; }
AvailabilityDomain getDomain() const { return Attr.getDomain(); }
StringRef getDomainName() const {
return getDomain().getNameForDiagnostics();
}
bool shouldHideDomainNameForConstraintDiagnostic(
const AvailabilityConstraint &constraint) {
switch (constraint.getDomain().getKind()) {
case AvailabilityDomain::Kind::Universal:
case AvailabilityDomain::Kind::Embedded:
return true;
case AvailabilityDomain::Kind::Platform:
return false;

bool shouldHideDomainNameInUnversionedDiagnostics() const {
switch (getDomain().getKind()) {
case AvailabilityDomain::Kind::Universal:
case AvailabilityDomain::Kind::Embedded:
return true;
case AvailabilityDomain::Kind::Platform:
case AvailabilityDomain::Kind::PackageDescription:
case AvailabilityDomain::Kind::SwiftLanguage:
switch (constraint.getKind()) {
case AvailabilityConstraint::Kind::AlwaysUnavailable:
case AvailabilityConstraint::Kind::IntroducedInNewerVersion:
return false;

case AvailabilityDomain::Kind::PackageDescription:
case AvailabilityDomain::Kind::SwiftLanguage:
switch (DiagnosticStatus) {
case Status::AlwaysUnavailable:
return false;
case Status::IntroducedInVersion:
case Status::Obsoleted:
return true;
}
}
}
};

static std::optional<UnavailabilityDiagnosticInfo>
getExplicitUnavailabilityDiagnosticInfo(const Decl *decl,
const ExportContext &where) {
auto attr = where.shouldDiagnoseDeclAsUnavailable(decl);
if (!attr)
return std::nullopt;

ASTContext &ctx = decl->getASTContext();

switch (attr->getVersionAvailability(ctx)) {
case AvailableVersionComparison::Available:
case AvailableVersionComparison::PotentiallyUnavailable:
llvm_unreachable("These aren't considered unavailable");

case AvailableVersionComparison::Unavailable:
if ((attr->isSwiftLanguageModeSpecific() ||
attr->isPackageDescriptionVersionSpecific()) &&
attr->getIntroduced()) {
return UnavailabilityDiagnosticInfo(
UnavailabilityDiagnosticInfo::Status::IntroducedInVersion, *attr);
} else {
return UnavailabilityDiagnosticInfo(
UnavailabilityDiagnosticInfo::Status::AlwaysUnavailable, *attr);
case AvailabilityConstraint::Kind::RequiresVersion:
case AvailabilityConstraint::Kind::Obsoleted:
return true;
}
break;

case AvailableVersionComparison::Obsoleted:
return UnavailabilityDiagnosticInfo(
UnavailabilityDiagnosticInfo::Status::Obsoleted, *attr);
}
}

bool diagnoseExplicitUnavailability(
SourceLoc loc, const RootProtocolConformance *rootConf,
const ExtensionDecl *ext, const ExportContext &where,
bool warnIfConformanceUnavailablePreSwift6,
bool preconcurrency) {
// Invertible protocols are never unavailable.
if (rootConf->getProtocol()->getInvertibleProtocolKind())
bool diagnoseExplicitUnavailability(SourceLoc loc,
const AvailabilityConstraint &constraint,
const RootProtocolConformance *rootConf,
const ExtensionDecl *ext,
const ExportContext &where,
bool warnIfConformanceUnavailablePreSwift6,
bool preconcurrency) {
if (constraint.isConditionallySatisfiable())
return false;

auto diagnosticInfo = getExplicitUnavailabilityDiagnosticInfo(ext, where);
if (!diagnosticInfo)
// Invertible protocols are never unavailable.
if (rootConf->getProtocol()->getInvertibleProtocolKind())
return false;

ASTContext &ctx = ext->getASTContext();
auto &diags = ctx.Diags;

auto type = rootConf->getType();
auto proto = rootConf->getProtocol()->getDeclaredInterfaceType();
StringRef versionedPlatform = diagnosticInfo->getDomainName();
StringRef platform =
diagnosticInfo->shouldHideDomainNameInUnversionedDiagnostics()
? ""
: versionedPlatform;
auto attr = diagnosticInfo->getAttr();
auto domain = constraint.getDomain();
StringRef versionedPlatform = domain.getNameForDiagnostics();
StringRef platform = shouldHideDomainNameForConstraintDiagnostic(constraint)
? ""
: versionedPlatform;
auto attr = constraint.getAttr();

// Downgrade unavailable Sendable conformance diagnostics where
// appropriate.
Expand All @@ -3115,23 +3061,25 @@ bool diagnoseExplicitUnavailability(
.limitBehaviorWithPreconcurrency(behavior, preconcurrency)
.warnUntilSwiftVersionIf(warnIfConformanceUnavailablePreSwift6, 6);

switch (diagnosticInfo->getStatus()) {
case UnavailabilityDiagnosticInfo::Status::AlwaysUnavailable:
switch (constraint.getKind()) {
case AvailabilityConstraint::Kind::AlwaysUnavailable:
diags
.diagnose(ext, diag::conformance_availability_marked_unavailable, type,
proto)
.highlight(attr.getParsedAttr()->getRange());
break;
case UnavailabilityDiagnosticInfo::Status::IntroducedInVersion:
case AvailabilityConstraint::Kind::RequiresVersion:
diags.diagnose(ext, diag::conformance_availability_introduced_in_version,
type, proto, versionedPlatform, *attr.getIntroduced());
break;
case UnavailabilityDiagnosticInfo::Status::Obsoleted:
case AvailabilityConstraint::Kind::Obsoleted:
diags
.diagnose(ext, diag::conformance_availability_obsoleted, type, proto,
versionedPlatform, *attr.getObsoleted())
.highlight(attr.getParsedAttr()->getRange());
break;
case AvailabilityConstraint::Kind::IntroducedInNewerVersion:
llvm_unreachable("unexpected constraint");
}
return true;
}
Expand Down Expand Up @@ -3510,14 +3458,14 @@ static void checkFunctionConversionAvailability(Type srcType, Type destType,
}

bool diagnoseExplicitUnavailability(
const ValueDecl *D, SourceRange R, const ExportContext &Where,
DeclAvailabilityFlags Flags,
llvm::function_ref<void(InFlightDiagnostic &)> attachRenameFixIts) {
auto diagnosticInfo = getExplicitUnavailabilityDiagnosticInfo(D, Where);
if (!diagnosticInfo)
const ValueDecl *D, SourceRange R, const AvailabilityConstraint &constraint,
const ExportContext &Where, DeclAvailabilityFlags Flags,
llvm::function_ref<void(InFlightDiagnostic &, StringRef)>
attachRenameFixIts) {
if (constraint.isConditionallySatisfiable())
return false;

auto Attr = diagnosticInfo->getAttr();
auto Attr = constraint.getAttr();
if (Attr.getDomain().isSwiftLanguage() && !Attr.isVersionSpecific()) {
if (shouldAllowReferenceToUnavailableInSwiftDeclaration(D, Where))
return false;
Expand All @@ -3526,11 +3474,11 @@ bool diagnoseExplicitUnavailability(
SourceLoc Loc = R.Start;
ASTContext &ctx = D->getASTContext();
auto &diags = ctx.Diags;
StringRef versionedPlatform = diagnosticInfo->getDomainName();
StringRef platform =
diagnosticInfo->shouldHideDomainNameInUnversionedDiagnostics()
? ""
: versionedPlatform;
auto domain = constraint.getDomain();
StringRef versionedPlatform = domain.getNameForDiagnostics();
StringRef platform = shouldHideDomainNameForConstraintDiagnostic(constraint)
? ""
: versionedPlatform;

// TODO: Consider removing this.
// ObjC keypaths components weren't checked previously, so errors are demoted
Expand All @@ -3556,7 +3504,7 @@ bool diagnoseExplicitUnavailability(
D, replaceKind.has_value(), rawReplaceKind,
newName, EncodedMessage.Message);
diag.limitBehavior(limit);
attachRenameFixIts(diag);
attachRenameFixIts(diag, rename);
} else if (isSubscriptReturningString(D, ctx)) {
diags.diagnose(Loc, diag::availability_string_subscript_migration)
.highlight(R)
Expand All @@ -3578,23 +3526,26 @@ bool diagnoseExplicitUnavailability(
}

auto sourceRange = Attr.getParsedAttr()->getRange();
switch (diagnosticInfo->getStatus()) {
case UnavailabilityDiagnosticInfo::Status::AlwaysUnavailable:
switch (constraint.getKind()) {
case AvailabilityConstraint::Kind::AlwaysUnavailable:
diags.diagnose(D, diag::availability_marked_unavailable, D)
.highlight(sourceRange);
break;
case UnavailabilityDiagnosticInfo::Status::IntroducedInVersion:
case AvailabilityConstraint::Kind::RequiresVersion:
diags
.diagnose(D, diag::availability_introduced_in_version, D,
versionedPlatform, *Attr.getIntroduced())
.highlight(sourceRange);
break;
case UnavailabilityDiagnosticInfo::Status::Obsoleted:
case AvailabilityConstraint::Kind::Obsoleted:
diags
.diagnose(D, diag::availability_obsoleted, D, versionedPlatform,
*Attr.getObsoleted())
.highlight(sourceRange);
break;
case AvailabilityConstraint::Kind::IntroducedInNewerVersion:
llvm_unreachable("unexpected constraint");
break;
}
return true;
}
Expand Down Expand Up @@ -4216,9 +4167,8 @@ bool swift::diagnoseDeclAvailability(const ValueDecl *D, SourceRange R,
auto constraint =
getUnsatisfiedAvailabilityConstraint(D, Where.getAvailability());

if (constraint && !constraint->isConditionallySatisfiable()) {
// FIXME: diagnoseExplicitUnavailability should take an unmet requirement
if (diagnoseExplicitUnavailability(D, R, Where, call, Flags))
if (constraint) {
if (diagnoseExplicitUnavailability(D, R, *constraint, Where, call, Flags))
return true;
}

Expand Down Expand Up @@ -4738,11 +4688,9 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
auto constraint =
getUnsatisfiedAvailabilityConstraint(ext, where.getAvailability());
if (constraint) {
// FIXME: diagnoseExplicitUnavailability() should take unmet requirement
if (diagnoseExplicitUnavailability(
loc, rootConf, ext, where,
warnIfConformanceUnavailablePreSwift6,
preconcurrency)) {
if (diagnoseExplicitUnavailability(loc, *constraint, rootConf, ext, where,
warnIfConformanceUnavailablePreSwift6,
preconcurrency)) {
maybeEmitAssociatedTypeNote();
return true;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeCheckDeclOverride.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2222,7 +2222,7 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) {
return true;
}

// FIXME: Possibly should extend to more availability checking.
// FIXME: [availability] Possibly should extend to more availability checking.
auto unavailabilityStatusAndAttr =
checkOverrideUnavailability(override, base);
auto unavailableAttr = unavailabilityStatusAndAttr.second;
Expand Down