Merge pull request #34565 from slavapestov/conformance-availability-part1

Sema: (Mostly) check conformance availability
This commit is contained in:
Slava Pestov
2020-11-06 12:49:05 -05:00
committed by GitHub
11 changed files with 563 additions and 57 deletions

View File

@@ -182,46 +182,19 @@ private:
/// Records the reason a declaration is potentially unavailable.
class UnavailabilityReason {
public:
enum class Kind {
/// The declaration is potentially unavailable because it requires an OS
/// version range that is not guaranteed by the minimum deployment
/// target.
RequiresOSVersionRange,
/// The declaration is potentially unavailable because it is explicitly
/// weakly linked.
ExplicitlyWeakLinked
};
private:
// A value of None indicates the declaration is potentially unavailable
// because it is explicitly weak linked.
Optional<VersionRange> RequiredDeploymentRange;
VersionRange RequiredDeploymentRange;
UnavailabilityReason(const Optional<VersionRange> &RequiredDeploymentRange)
explicit UnavailabilityReason(const VersionRange RequiredDeploymentRange)
: RequiredDeploymentRange(RequiredDeploymentRange) {}
public:
static UnavailabilityReason explicitlyWeaklyLinked() {
return UnavailabilityReason(None);
}
static UnavailabilityReason requiresVersionRange(const VersionRange Range) {
return UnavailabilityReason(Range);
}
Kind getReasonKind() const {
if (RequiredDeploymentRange.hasValue()) {
return Kind::RequiresOSVersionRange;
} else {
return Kind::ExplicitlyWeakLinked;
}
}
const VersionRange &getRequiredOSVersionRange() const {
assert(getReasonKind() == Kind::RequiresOSVersionRange);
return RequiredDeploymentRange.getValue();
return RequiredDeploymentRange;
}
};

View File

@@ -5040,6 +5040,39 @@ ERROR(availabilty_string_subscript_migration, none,
"subscripts returning String were obsoleted in Swift 4; explicitly "
"construct a String from subscripted result", ())
// Conformance availability checking diagnostics
ERROR(conformance_availability_unavailable, none,
"conformance of %0 to %1 is unavailable"
"%select{ in %3|}2%select{|: %4}4",
(Type, Type, bool, StringRef, StringRef))
NOTE(conformance_availability_marked_unavailable, none,
"conformance of %0 to %1 has been explicitly marked "
"unavailable here", (Type, Type))
NOTE(conformance_availability_introduced_in_version, none,
"conformance of %0 to %1 was introduced in %2 %3",
(Type, Type, StringRef, llvm::VersionTuple))
NOTE(conformance_availability_obsoleted, none,
"conformance of %0 to %1 was obsoleted in %2 %3",
(Type, Type, StringRef, llvm::VersionTuple))
WARNING(conformance_availability_deprecated, none,
"conformance of %0 to %1 %select{is|%select{is|was}4}2 "
"deprecated%select{| in %3%select{| %5}4}2%select{|: %6}6",
(Type, Type, bool, StringRef, bool, llvm::VersionTuple,
StringRef))
ERROR(conformance_availability_only_version_newer, none,
"conformance of %0 to %1 is only available in %2 %3 or newer",
(Type, Type, StringRef, llvm::VersionTuple))
WARNING(conformance_availability_only_version_newer_warn, none,
"conformance of %0 to %1 is only available in %2 %3 or newer",
(Type, Type, StringRef, llvm::VersionTuple))
//------------------------------------------------------------------------------
// MARK: @discardableResult
//------------------------------------------------------------------------------

View File

@@ -95,6 +95,9 @@ namespace swift {
/// Disable API availability checking.
bool DisableAvailabilityChecking = false;
/// Should conformance availability violations be diagnosed as errors?
bool EnableConformanceAvailabilityErrors = false;
/// Maximum number of typo corrections we are allowed to perform.
/// This is disabled by default until we can get typo-correction working within acceptable performance bounds.
unsigned TypoCorrectionLimit = 0;

View File

@@ -405,6 +405,14 @@ def disable_availability_checking : Flag<["-"],
"disable-availability-checking">,
HelpText<"Disable checking for potentially unavailable APIs">;
def enable_conformance_availability_errors : Flag<["-"],
"enable-conformance-availability-errors">,
HelpText<"Diagnose conformance availability violations as errors">;
def disable_conformance_availability_errors : Flag<["-"],
"disable-conformance-availability-errors">,
HelpText<"Diagnose conformance availability violations as warnings">;
def report_errors_to_debugger : Flag<["-"], "report-errors-to-debugger">,
HelpText<"Deprecated, will be removed in future versions.">;

View File

@@ -391,6 +391,12 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
Opts.DisableAvailabilityChecking |=
Args.hasArg(OPT_disable_availability_checking);
if (auto A = Args.getLastArg(OPT_enable_conformance_availability_errors,
OPT_disable_conformance_availability_errors)) {
Opts.EnableConformanceAvailabilityErrors
= A->getOption().matches(OPT_enable_conformance_availability_errors);
}
if (auto A = Args.getLastArg(OPT_enable_access_control,
OPT_disable_access_control)) {
Opts.EnableAccessControl

View File

@@ -160,17 +160,16 @@ TypeChecker::diagnoseDeclRefExportability(SourceLoc loc,
bool
TypeChecker::diagnoseConformanceExportability(SourceLoc loc,
const RootProtocolConformance *rootConf,
const ExtensionDecl *ext,
const ExportContext &where) {
if (!where.mustOnlyReferenceExportedDecls())
return false;
auto originKind = getDisallowedOriginKind(
rootConf->getDeclContext()->getAsDecl(),
where);
auto originKind = getDisallowedOriginKind(ext, where);
if (originKind == DisallowedOriginKind::None)
return false;
ModuleDecl *M = rootConf->getDeclContext()->getParentModule();
ModuleDecl *M = ext->getParentModule();
ASTContext &ctx = M->getASTContext();
auto reason = where.getExportabilityReason();

View File

@@ -958,6 +958,13 @@ TypeChecker::checkDeclarationAvailability(const Decl *D,
return UnavailabilityReason::requiresVersionRange(version);
}
Optional<UnavailabilityReason>
TypeChecker::checkConformanceAvailability(const RootProtocolConformance *conf,
const ExtensionDecl *ext,
const ExportContext &where) {
return checkDeclarationAvailability(ext, where);
}
/// A class that walks the AST to find the innermost (i.e., deepest) node that
/// contains a target SourceRange and matches a particular criterion.
/// This class finds the innermost nodes of interest by walking
@@ -1563,13 +1570,6 @@ void TypeChecker::diagnosePotentialOpaqueTypeUnavailability(
const UnavailabilityReason &Reason) {
ASTContext &Context = ReferenceDC->getASTContext();
// We only emit diagnostics for API unavailability, not for explicitly
// weak-linked symbols.
if (Reason.getReasonKind() !=
UnavailabilityReason::Kind::RequiresOSVersionRange) {
return;
}
auto RequiredRange = Reason.getRequiredOSVersionRange();
{
auto Err =
@@ -1593,13 +1593,6 @@ void TypeChecker::diagnosePotentialUnavailability(
const UnavailabilityReason &Reason) {
ASTContext &Context = ReferenceDC->getASTContext();
// We only emit diagnostics for API unavailability, not for explicitly
// weak-linked symbols.
if (Reason.getReasonKind() !=
UnavailabilityReason::Kind::RequiresOSVersionRange) {
return;
}
auto RequiredRange = Reason.getRequiredOSVersionRange();
{
auto Err =
@@ -1652,6 +1645,37 @@ void TypeChecker::diagnosePotentialAccessorUnavailability(
fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, Context);
}
void TypeChecker::diagnosePotentialUnavailability(
const RootProtocolConformance *rootConf,
const ExtensionDecl *ext,
SourceLoc loc,
const DeclContext *dc,
const UnavailabilityReason &reason) {
ASTContext &ctx = dc->getASTContext();
auto requiredRange = reason.getRequiredOSVersionRange();
{
auto type = rootConf->getType();
auto proto = rootConf->getProtocol()->getDeclaredInterfaceType();
auto diagID = (ctx.LangOpts.EnableConformanceAvailabilityErrors
? diag::conformance_availability_only_version_newer
: diag::conformance_availability_only_version_newer_warn);
auto err =
ctx.Diags.diagnose(
loc, diagID,
type, proto, prettyPlatformString(targetPlatform(ctx.LangOpts)),
reason.getRequiredOSVersionRange().getLowerEndpoint());
// Direct a fixit to the error if an existing guard is nearly-correct
if (fixAvailabilityByNarrowingNearbyVersionCheck(loc, dc,
requiredRange, ctx, err))
return;
}
fixAvailability(loc, dc, requiredRange, ctx);
}
const AvailableAttr *TypeChecker::getDeprecated(const Decl *D) {
if (auto *Attr = D->getAttrs().getDeprecated(D->getASTContext()))
return Attr;
@@ -1669,7 +1693,7 @@ const AvailableAttr *TypeChecker::getDeprecated(const Decl *D) {
/// Returns true if the reference or any of its parents is an
/// unconditional unavailable declaration for the same platform.
static bool isInsideCompatibleUnavailableDeclaration(
const ValueDecl *D, const ExportContext &where,
const Decl *D, const ExportContext &where,
const AvailableAttr *attr) {
auto referencedPlatform = where.getUnavailablePlatformKind();
if (!referencedPlatform)
@@ -2148,6 +2172,60 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange,
}
}
void TypeChecker::diagnoseIfDeprecated(
SourceLoc loc,
const RootProtocolConformance *rootConf,
const ExtensionDecl *ext,
const ExportContext &where) {
const AvailableAttr *attr = TypeChecker::getDeprecated(ext);
if (!attr)
return;
// We match the behavior of clang to not report deprecation warnings
// inside declarations that are themselves deprecated on all deployment
// targets.
if (where.isDeprecated()) {
return;
}
auto *dc = where.getDeclContext();
auto &ctx = dc->getASTContext();
if (!ctx.LangOpts.DisableAvailabilityChecking) {
AvailabilityContext runningOSVersion = where.getAvailabilityContext();
if (runningOSVersion.isKnownUnreachable()) {
// Suppress a deprecation warning if the availability checking machinery
// thinks the reference program location will not execute on any
// deployment target for the current platform.
return;
}
}
auto type = rootConf->getType();
auto proto = rootConf->getProtocol()->getDeclaredInterfaceType();
StringRef platform = attr->prettyPlatformString();
llvm::VersionTuple deprecatedVersion;
if (attr->Deprecated)
deprecatedVersion = attr->Deprecated.getValue();
if (attr->Message.empty()) {
ctx.Diags.diagnose(
loc, diag::conformance_availability_deprecated,
type, proto, attr->hasPlatform(), platform,
attr->Deprecated.hasValue(), deprecatedVersion,
/*message*/ StringRef())
.highlight(attr->getRange());
return;
}
EncodedDiagnosticMessage encodedMessage(attr->Message);
ctx.Diags.diagnose(
loc, diag::conformance_availability_deprecated,
type, proto, attr->hasPlatform(), platform,
attr->Deprecated.hasValue(), deprecatedVersion,
encodedMessage.Message)
.highlight(attr->getRange());
}
void swift::diagnoseUnavailableOverride(ValueDecl *override,
const ValueDecl *base,
@@ -2212,6 +2290,102 @@ bool swift::diagnoseExplicitUnavailability(const ValueDecl *D,
});
}
/// Emit a diagnostic for references to declarations that have been
/// marked as unavailable, either through "unavailable" or "obsoleted:".
bool swift::diagnoseExplicitUnavailability(SourceLoc loc,
const RootProtocolConformance *rootConf,
const ExtensionDecl *ext,
const ExportContext &where) {
auto *attr = AvailableAttr::isUnavailable(ext);
if (!attr)
return false;
// Calling unavailable code from within code with the same
// unavailability is OK -- the eventual caller can't call the
// enclosing code in the same situations it wouldn't be able to
// call this code.
if (isInsideCompatibleUnavailableDeclaration(ext, where, attr))
return false;
ASTContext &ctx = ext->getASTContext();
auto &diags = ctx.Diags;
auto type = rootConf->getType();
auto proto = rootConf->getProtocol()->getDeclaredInterfaceType();
StringRef platform;
switch (attr->getPlatformAgnosticAvailability()) {
case PlatformAgnosticAvailabilityKind::Deprecated:
llvm_unreachable("shouldn't see deprecations in explicit unavailability");
case PlatformAgnosticAvailabilityKind::None:
case PlatformAgnosticAvailabilityKind::Unavailable:
if (attr->Platform != PlatformKind::none) {
// This was platform-specific; indicate the platform.
platform = attr->prettyPlatformString();
break;
}
LLVM_FALLTHROUGH;
case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific:
case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific:
// We don't want to give further detail about these.
platform = "";
break;
case PlatformAgnosticAvailabilityKind::UnavailableInSwift:
// This API is explicitly unavailable in Swift.
platform = "Swift";
break;
}
EncodedDiagnosticMessage EncodedMessage(attr->Message);
diags.diagnose(loc, diag::conformance_availability_unavailable,
type, proto,
platform.empty(), platform, EncodedMessage.Message);
switch (attr->getVersionAvailability(ctx)) {
case AvailableVersionComparison::Available:
case AvailableVersionComparison::PotentiallyUnavailable:
llvm_unreachable("These aren't considered unavailable");
case AvailableVersionComparison::Unavailable:
if ((attr->isLanguageVersionSpecific() ||
attr->isPackageDescriptionVersionSpecific())
&& attr->Introduced.hasValue())
diags.diagnose(ext, diag::conformance_availability_introduced_in_version,
type, proto,
(attr->isLanguageVersionSpecific() ?
"Swift" : "PackageDescription"),
*attr->Introduced)
.highlight(attr->getRange());
else
diags.diagnose(ext, diag::conformance_availability_marked_unavailable,
type, proto)
.highlight(attr->getRange());
break;
case AvailableVersionComparison::Obsoleted:
// FIXME: Use of the platformString here is non-awesome for application
// extensions.
StringRef platformDisplayString;
if (attr->isLanguageVersionSpecific()) {
platformDisplayString = "Swift";
} else if (attr->isPackageDescriptionVersionSpecific()) {
platformDisplayString = "PackageDescription";
} else {
platformDisplayString = platform;
}
diags.diagnose(ext, diag::conformance_availability_obsoleted,
type, proto, platformDisplayString, *attr->Obsoleted)
.highlight(attr->getRange());
break;
}
return true;
}
/// Check if this is a subscript declaration inside String or
/// Substring that returns String, and if so return true.
bool isSubscriptReturningString(const ValueDecl *D, ASTContext &Context) {
@@ -3201,19 +3375,42 @@ bool
swift::diagnoseConformanceAvailability(SourceLoc loc,
ProtocolConformanceRef conformance,
const ExportContext &where) {
assert(!where.isImplicit());
if (!conformance.isConcrete())
return false;
const ProtocolConformance *concreteConf = conformance.getConcrete();
const RootProtocolConformance *rootConf = concreteConf->getRootConformance();
auto *DC = where.getDeclContext();
if (auto *ext = dyn_cast<ExtensionDecl>(rootConf->getDeclContext())) {
if (TypeChecker::diagnoseConformanceExportability(loc, rootConf, ext, where))
return true;
if (diagnoseExplicitUnavailability(loc, rootConf, ext, where))
return true;
// Diagnose for deprecation
TypeChecker::diagnoseIfDeprecated(loc, rootConf, ext, where);
// Diagnose (and possibly signal) for potential unavailability
auto maybeUnavail = TypeChecker::checkConformanceAvailability(
rootConf, ext, where);
if (maybeUnavail.hasValue()) {
TypeChecker::diagnosePotentialUnavailability(rootConf, ext, loc, DC,
maybeUnavail.getValue());
}
}
// Now, check associated conformances.
SubstitutionMap subConformanceSubs =
concreteConf->getSubstitutions(DC->getParentModule());
diagnoseSubstitutionMapAvailability(loc, subConformanceSubs, where);
const RootProtocolConformance *rootConf =
concreteConf->getRootConformance();
if (diagnoseSubstitutionMapAvailability(loc, subConformanceSubs, where))
return true;
return TypeChecker::diagnoseConformanceExportability(
loc, rootConf, where);
return false;
}
bool

View File

@@ -30,6 +30,7 @@ namespace swift {
class InFlightDiagnostic;
class Decl;
class ProtocolConformanceRef;
class RootProtocolConformance;
class Stmt;
class SubstitutionMap;
class Type;
@@ -247,6 +248,14 @@ bool diagnoseExplicitUnavailability(
DeclAvailabilityFlags Flags,
llvm::function_ref<void(InFlightDiagnostic &)> attachRenameFixIts);
/// Emit a diagnostic for references to declarations that have been
/// marked as unavailable, either through "unavailable" or "obsoleted:".
bool diagnoseExplicitUnavailability(
SourceLoc loc,
const RootProtocolConformance *rootConf,
const ExtensionDecl *ext,
const ExportContext &where);
/// Check if \p decl has a introduction version required by -require-explicit-availability
void checkExplicitAvailability(Decl *decl);

View File

@@ -966,6 +966,7 @@ bool diagnoseDeclRefExportability(SourceLoc loc,
/// conformance is SPI or visible via an implementation-only import.
bool diagnoseConformanceExportability(SourceLoc loc,
const RootProtocolConformance *rootConf,
const ExtensionDecl *ext,
const ExportContext &where);
/// \name Availability checking
@@ -1010,7 +1011,16 @@ diagnosticIfDeclCannotBePotentiallyUnavailable(const Decl *D);
/// declaration is unavailable. Returns None is the declaration is
/// definitely available.
Optional<UnavailabilityReason>
checkDeclarationAvailability(const Decl *D, const ExportContext &where);
checkDeclarationAvailability(const Decl *D, const ExportContext &Where);
/// Checks whether a conformance should be considered unavailable when
/// referred to at the given location and, if so, returns the reason why the
/// declaration is unavailable. Returns None is the declaration is
/// definitely available.
Optional<UnavailabilityReason>
checkConformanceAvailability(const RootProtocolConformance *Conf,
const ExtensionDecl *Ext,
const ExportContext &Where);
/// Checks an "ignored" expression to see if it's okay for it to be ignored.
///
@@ -1025,6 +1035,14 @@ void diagnosePotentialUnavailability(const ValueDecl *D,
const DeclContext *ReferenceDC,
const UnavailabilityReason &Reason);
// Emits a diagnostic, if necessary, for a reference to a declaration
// that is potentially unavailable at the given source location.
void diagnosePotentialUnavailability(const RootProtocolConformance *rootConf,
const ExtensionDecl *ext,
SourceLoc loc,
const DeclContext *dc,
const UnavailabilityReason &reason);
void
diagnosePotentialOpaqueTypeUnavailability(SourceRange ReferenceRange,
const DeclContext *ReferenceDC,
@@ -1042,12 +1060,16 @@ void diagnosePotentialAccessorUnavailability(
const AvailableAttr *getDeprecated(const Decl *D);
/// Emits a diagnostic for a reference to a declaration that is deprecated.
/// Callers can provide a lambda that adds additional information (such as a
/// fixit hint) to the deprecation diagnostic, if it is emitted.
void diagnoseIfDeprecated(SourceRange SourceRange,
const ExportContext &Where,
const ValueDecl *DeprecatedDecl,
const ApplyExpr *Call);
/// Emits a diagnostic for a reference to a conformnace that is deprecated.
void diagnoseIfDeprecated(SourceLoc Loc,
const RootProtocolConformance *DeprecatedConf,
const ExtensionDecl *Ext,
const ExportContext &Where);
/// @}
/// If LangOptions::DebugForbidTypecheckPrefix is set and the given decl

View File

@@ -0,0 +1,215 @@
// RUN: %target-typecheck-verify-swift -swift-version 5 -enable-conformance-availability-errors
// REQUIRES: OS=macosx
public protocol Horse {}
func takesHorse<T : Horse>(_: T) {}
extension Horse {
func giddyUp() {}
}
struct UsesHorse<T : Horse> {}
// Unconditional unavailability
public struct HasUnavailableConformance1 {}
@available(*, unavailable)
extension HasUnavailableConformance1 : Horse {}
// expected-note@-1 6{{conformance of 'HasUnavailableConformance1' to 'Horse' has been explicitly marked unavailable here}}
func passUnavailableConformance1(x: HasUnavailableConformance1) {
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
_ = UsesHorse<HasUnavailableConformance1>.self // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
}
@available(*, unavailable)
func passUnavailableConformance1a(x: HasUnavailableConformance1) {
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
_ = UsesHorse<HasUnavailableConformance1>.self // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
}
// Platform unavailability
public struct HasUnavailableConformance2 {}
@available(macOS, unavailable)
extension HasUnavailableConformance2 : Horse {}
// expected-note@-1 3{{conformance of 'HasUnavailableConformance2' to 'Horse' has been explicitly marked unavailable here}}
func passUnavailableConformance2(x: HasUnavailableConformance2) {
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance2' to 'Horse' is unavailable in macOS}}
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance2' to 'Horse' is unavailable in macOS}}
_ = UsesHorse<HasUnavailableConformance2>.self // expected-error {{conformance of 'HasUnavailableConformance2' to 'Horse' is unavailable in macOS}}
}
@available(macOS, unavailable)
func passUnavailableConformance2a(x: HasUnavailableConformance2) {
// This is allowed
takesHorse(x)
x.giddyUp()
}
// Swift version unavailability
public struct HasUnavailableConformance3 {}
@available(swift 12)
extension HasUnavailableConformance3 : Horse {}
// expected-note@-1 6{{conformance of 'HasUnavailableConformance3' to 'Horse' was introduced in Swift 12}}
func passUnavailableConformance3(x: HasUnavailableConformance3) {
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
_ = UsesHorse<HasUnavailableConformance3>.self // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
}
@available(swift 12)
func passUnavailableConformance3a(x: HasUnavailableConformance3) {
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
_ = UsesHorse<HasUnavailableConformance3>.self // expected-error {{conformance of 'HasUnavailableConformance3' to 'Horse' is unavailable}}
}
// Platform obsoleted
public struct HasUnavailableConformance4 {}
@available(macOS, obsoleted: 10.1)
extension HasUnavailableConformance4 : Horse {}
// expected-note@-1 6{{conformance of 'HasUnavailableConformance4' to 'Horse' was obsoleted in macOS 10.1}}
func passUnavailableConformance4(x: HasUnavailableConformance4) {
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
_ = UsesHorse<HasUnavailableConformance4>.self // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
}
@available(macOS, obsoleted: 10.1)
func passUnavailableConformance4a(x: HasUnavailableConformance4) {
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
_ = UsesHorse<HasUnavailableConformance4>.self // expected-error {{conformance of 'HasUnavailableConformance4' to 'Horse' is unavailable in macOS}}
}
// Swift obsoleted
public struct HasUnavailableConformance5 {}
@available(swift, obsoleted: 4)
extension HasUnavailableConformance5 : Horse {}
// expected-note@-1 6{{conformance of 'HasUnavailableConformance5' to 'Horse' was obsoleted in Swift 4}}
func passUnavailableConformance5(x: HasUnavailableConformance5) {
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
_ = UsesHorse<HasUnavailableConformance5>.self // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
}
@available(swift, obsoleted: 4)
func passUnavailableConformance5a(x: HasUnavailableConformance5) {
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
_ = UsesHorse<HasUnavailableConformance5>.self // expected-error {{conformance of 'HasUnavailableConformance5' to 'Horse' is unavailable}}
}
// Unavailable with message
public struct HasUnavailableConformance6 {}
@available(*, unavailable, message: "This conformance is bad")
extension HasUnavailableConformance6 : Horse {}
// expected-note@-1 3{{conformance of 'HasUnavailableConformance6' to 'Horse' has been explicitly marked unavailable here}}
func passUnavailableConformance6(x: HasUnavailableConformance6) {
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance6' to 'Horse' is unavailable: This conformance is bad}}
x.giddyUp() // expected-error {{conformance of 'HasUnavailableConformance6' to 'Horse' is unavailable: This conformance is bad}}
_ = UsesHorse<HasUnavailableConformance6>.self // expected-error {{conformance of 'HasUnavailableConformance6' to 'Horse' is unavailable: This conformance is bad}}
}
// Deprecated
public struct HasDeprecatedConformance1 {}
@available(*, deprecated)
extension HasDeprecatedConformance1 : Horse {}
func passDeprecatedConformance1(x: HasDeprecatedConformance1) {
takesHorse(x) // expected-warning {{conformance of 'HasDeprecatedConformance1' to 'Horse' is deprecated}}
x.giddyUp() // expected-warning {{conformance of 'HasDeprecatedConformance1' to 'Horse' is deprecated}}
_ = UsesHorse<HasDeprecatedConformance1>.self // expected-warning {{conformance of 'HasDeprecatedConformance1' to 'Horse' is deprecated}}
}
@available(*, deprecated)
func passDeprecatedConformance1a(x: HasDeprecatedConformance1) {
takesHorse(x)
x.giddyUp()
_ = UsesHorse<HasDeprecatedConformance1>.self
}
// Deprecated with message
public struct HasDeprecatedConformance2 {}
@available(*, deprecated, message: "This conformance is deprecated")
extension HasDeprecatedConformance2 : Horse {}
func passDeprecatedConformance2(x: HasDeprecatedConformance2) {
takesHorse(x) // expected-warning {{conformance of 'HasDeprecatedConformance2' to 'Horse' is deprecated: This conformance is deprecated}}
x.giddyUp() // expected-warning {{conformance of 'HasDeprecatedConformance2' to 'Horse' is deprecated: This conformance is deprecated}}
_ = UsesHorse<HasDeprecatedConformance2>.self // expected-warning {{conformance of 'HasDeprecatedConformance2' to 'Horse' is deprecated: This conformance is deprecated}}
}
@available(*, deprecated)
func passDeprecatedConformance2a(x: HasDeprecatedConformance2) {
takesHorse(x)
x.giddyUp()
_ = UsesHorse<HasDeprecatedConformance2>.self
}
// Deprecated with version
public struct HasDeprecatedConformance3 {}
@available(macOS, introduced: 10.7, deprecated: 10.8)
extension HasDeprecatedConformance3 : Horse {}
func passDeprecatedConformance3(x: HasDeprecatedConformance3) {
takesHorse(x) // expected-warning {{conformance of 'HasDeprecatedConformance3' to 'Horse' was deprecated in macOS 10.8}}
x.giddyUp() // expected-warning {{conformance of 'HasDeprecatedConformance3' to 'Horse' was deprecated in macOS 10.8}}
_ = UsesHorse<HasDeprecatedConformance3>.self // expected-warning {{conformance of 'HasDeprecatedConformance3' to 'Horse' was deprecated in macOS 10.8}}
}
func passDeprecatedConformance3a(x: HasDeprecatedConformance3) {
if #available(macOS 10.8, *) {
} else {
// This branch is dead with our minimum deployment target, so don't emit
// deprecation diagnostics in it.
takesHorse(x)
x.giddyUp()
_ = UsesHorse<HasDeprecatedConformance3>.self
}
}
// Availability with version
public struct HasAvailableConformance1 {}
@available(macOS 100, *)
extension HasAvailableConformance1 : Horse {}
// These availability violations are errors because this test passes the
// -enable-conformance-availability-errors flag. See the other test case
// in test/Sema/conformance_availability_warn.swift for the same example
// but without this flag.
func passAvailableConformance1(x: HasAvailableConformance1) { // expected-note 3{{add @available attribute to enclosing global function}}
takesHorse(x) // expected-error {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
// expected-note@-1 {{add 'if #available' version check}}
x.giddyUp() // expected-error {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
// expected-note@-1 {{add 'if #available' version check}}
_ = UsesHorse<HasAvailableConformance1>.self // expected-error {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
// expected-note@-1 {{add 'if #available' version check}}
}
@available(macOS 100, *)
func passAvailableConformance1a(x: HasAvailableConformance1) {
takesHorse(x)
x.giddyUp()
_ = UsesHorse<HasAvailableConformance1>.self
}

View File

@@ -0,0 +1,41 @@
// RUN: %target-typecheck-verify-swift -swift-version 5
// REQUIRES: OS=macosx
public protocol Horse {}
func takesHorse<T : Horse>(_: T) {}
extension Horse {
func giddyUp() {}
}
struct UsesHorse<T : Horse> {}
// Availability with version
public struct HasAvailableConformance1 {}
@available(macOS 100, *)
extension HasAvailableConformance1 : Horse {}
// These availability violations are warnings because this test does not
// pass the -enable-conformance-availability-errors flag. See the other
// test case in test/Sema/conformance_availability.swift for the same
// example but with this flag.
func passAvailableConformance1(x: HasAvailableConformance1) { // expected-note 3{{add @available attribute to enclosing global function}}
takesHorse(x) // expected-warning {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
// expected-note@-1 {{add 'if #available' version check}}
x.giddyUp() // expected-warning {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
// expected-note@-1 {{add 'if #available' version check}}
_ = UsesHorse<HasAvailableConformance1>.self // expected-warning {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
// expected-note@-1 {{add 'if #available' version check}}
}
@available(macOS 100, *)
func passAvailableConformance1a(x: HasAvailableConformance1) {
takesHorse(x)
x.giddyUp()
_ = UsesHorse<HasAvailableConformance1>.self
}