mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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:
@@ -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
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.">;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
215
test/Sema/conformance_availability.swift
Normal file
215
test/Sema/conformance_availability.swift
Normal 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
|
||||
}
|
||||
41
test/Sema/conformance_availability_warn.swift
Normal file
41
test/Sema/conformance_availability_warn.swift
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user