Sema: (Mostly) check conformance availability

If a conformance is defined in an extension, we now look for
references to the conformance in types and expressions and
respect's the extension's availability (or deprecation, etc).

The conformance checker itself still needs to check conformance
availability of associated conformances and the like; that will
be a separate change.

Note that conformances defined on types don't require any
special handling, since they are as available as the
intersection of the conforming type and the protocol.

By default, we diagnose conformance availability violations
where the OS version is not sufficiently new as warnings, to
avoid breaking source compatibility. Stricter behavior where
these violations are diagnosed as errors is enabled by passing
the -enable-conformance-availability-errors flag. There are
test cases that run both with and without this flag. In the
future, we hope to make the stricter behavior the default,
since after all, violations here can result in link errors and
runtime crashes.

Uses of completely unavailable conformances are still always
diagnosed as errors, even when this flag is not passed in.

Progress on <rdar://problem/35158274>.
This commit is contained in:
Slava Pestov
2020-10-29 14:23:23 -04:00
parent f150bdd785
commit ceb8675ad1
10 changed files with 560 additions and 13 deletions

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

@@ -94,6 +94,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
@@ -1638,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;
@@ -1655,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)
@@ -2134,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,
@@ -2198,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) {
@@ -3187,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
}