mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Sema: Check custom domain availability during conformance checking.
Protocol requirement witnesses cannot only be available in a custom availability domain if the requirement does not have the same availability constraint. Resolves rdar://156462516.
This commit is contained in:
@@ -7123,6 +7123,11 @@ ERROR(availability_protocol_requires_version,
|
||||
(const ProtocolDecl *, const ValueDecl *, AvailabilityDomain,
|
||||
AvailabilityRange))
|
||||
|
||||
ERROR(availability_protocol_requirement_only_available_in,
|
||||
none, "protocol %0 requirement %1 cannot be satisfied by %kindonly1 that "
|
||||
"is only available in %2",
|
||||
(const ProtocolDecl *, const ValueDecl *, AvailabilityDomain))
|
||||
|
||||
NOTE(availability_protocol_requirement_here, none,
|
||||
"protocol requirement here", ())
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#ifndef SWIFT_AST_REQUIREMENTMATCH_H
|
||||
#define SWIFT_AST_REQUIREMENTMATCH_H
|
||||
|
||||
#include "swift/AST/AvailabilityConstraint.h"
|
||||
#include "swift/AST/RequirementEnvironment.h"
|
||||
#include "swift/AST/Type.h"
|
||||
#include "swift/AST/Types.h"
|
||||
@@ -252,7 +253,8 @@ class RequirementCheck {
|
||||
|
||||
/// Storage for `CheckKind::Availability`.
|
||||
struct {
|
||||
AvailabilityRange requiredRange;
|
||||
AvailabilityConstraint constraint;
|
||||
AvailabilityContext requiredContext;
|
||||
} Availability;
|
||||
};
|
||||
|
||||
@@ -266,8 +268,10 @@ public:
|
||||
RequirementCheck(AccessScope requiredAccessScope, bool forSetter)
|
||||
: Kind(CheckKind::Access), Access{requiredAccessScope, forSetter} {}
|
||||
|
||||
RequirementCheck(AvailabilityRange requiredRange)
|
||||
: Kind(CheckKind::Availability), Availability{requiredRange} {}
|
||||
RequirementCheck(AvailabilityConstraint constraint,
|
||||
AvailabilityContext requiredContext)
|
||||
: Kind(CheckKind::Availability),
|
||||
Availability{constraint, requiredContext} {}
|
||||
|
||||
CheckKind getKind() const { return Kind; }
|
||||
|
||||
@@ -280,7 +284,7 @@ public:
|
||||
/// True if the witness is less available than the requirement.
|
||||
bool isLessAvailable() const {
|
||||
return (Kind == CheckKind::Availability)
|
||||
? !Availability.requiredRange.isKnownUnreachable()
|
||||
? !Availability.constraint.isUnavailable()
|
||||
: false;
|
||||
}
|
||||
|
||||
@@ -291,11 +295,18 @@ public:
|
||||
return Access.requiredScope;
|
||||
}
|
||||
|
||||
/// The availability constraint that would fail if the witness were accessed
|
||||
/// from contexts in which the requirement is available.
|
||||
AvailabilityConstraint getAvailabilityConstraint() const {
|
||||
ASSERT(Kind == CheckKind::Availability);
|
||||
return Availability.constraint;
|
||||
}
|
||||
|
||||
/// The required availability range for checks that failed due to the witness
|
||||
/// being less available than the requirement.
|
||||
AvailabilityRange getRequiredAvailabilityRange() const {
|
||||
AvailabilityContext getRequiredAvailabilityContext() const {
|
||||
ASSERT(Kind == CheckKind::Availability);
|
||||
return Availability.requiredRange;
|
||||
return Availability.requiredContext;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1670,72 +1670,6 @@ SelfAccessKindRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const {
|
||||
return SelfAccessKind::NonMutating;
|
||||
}
|
||||
|
||||
bool TypeChecker::isAvailabilitySafeForConformance(
|
||||
const ProtocolDecl *proto, const ValueDecl *requirement,
|
||||
const ValueDecl *witness, const DeclContext *dc,
|
||||
AvailabilityRange &requirementInfo) {
|
||||
|
||||
// We assume conformances in
|
||||
// non-SourceFiles have already been checked for availability.
|
||||
if (!dc->getParentSourceFile())
|
||||
return true;
|
||||
|
||||
auto &Context = proto->getASTContext();
|
||||
assert(dc->getSelfNominalTypeDecl() &&
|
||||
"Must have a nominal or extension context");
|
||||
|
||||
auto contextForConformingDecl =
|
||||
AvailabilityContext::forDeclSignature(dc->getAsDecl());
|
||||
|
||||
// If the conformance is unavailable then it's irrelevant whether the witness
|
||||
// is potentially unavailable.
|
||||
if (contextForConformingDecl.isUnavailable())
|
||||
return true;
|
||||
|
||||
// Make sure that any access of the witness through the protocol
|
||||
// can only occur when the witness is available. That is, make sure that
|
||||
// on every version where the conforming declaration is available, if the
|
||||
// requirement is available then the witness is available as well.
|
||||
// We do this by checking that (an over-approximation of) the intersection of
|
||||
// the requirement's available range with both the conforming declaration's
|
||||
// available range and the protocol's available range is fully contained in
|
||||
// (an over-approximation of) the intersection of the witnesses's available
|
||||
// range with both the conforming type's available range and the protocol
|
||||
// declaration's available range.
|
||||
AvailabilityRange witnessInfo =
|
||||
AvailabilityInference::availableRange(witness);
|
||||
requirementInfo = AvailabilityInference::availableRange(requirement);
|
||||
|
||||
AvailabilityRange infoForConformingDecl =
|
||||
contextForConformingDecl.getPlatformRange();
|
||||
|
||||
// Relax the requirements for @_spi witnesses by treating the requirement as
|
||||
// if it were introduced at the deployment target. This is not strictly sound
|
||||
// since clients of SPI do not necessarily have the same deployment target as
|
||||
// the module declaring the requirement. However, now that the public
|
||||
// declarations in API libraries are checked according to the minimum possible
|
||||
// deployment target of their clients this relaxation is needed for source
|
||||
// compatibility with some existing code and is reasonably safe for the
|
||||
// majority of cases.
|
||||
if (witness->isSPI()) {
|
||||
AvailabilityRange deploymentTarget =
|
||||
AvailabilityRange::forDeploymentTarget(Context);
|
||||
requirementInfo.constrainWith(deploymentTarget);
|
||||
}
|
||||
|
||||
// Constrain over-approximates intersection of version ranges.
|
||||
witnessInfo.constrainWith(infoForConformingDecl);
|
||||
requirementInfo.constrainWith(infoForConformingDecl);
|
||||
|
||||
AvailabilityRange infoForProtocolDecl =
|
||||
AvailabilityContext::forDeclSignature(proto).getPlatformRange();
|
||||
|
||||
witnessInfo.constrainWith(infoForProtocolDecl);
|
||||
requirementInfo.constrainWith(infoForProtocolDecl);
|
||||
|
||||
return requirementInfo.isContainedIn(witnessInfo);
|
||||
}
|
||||
|
||||
// Returns 'nullptr' if this is the 'newValue' or 'oldValue' parameter;
|
||||
// otherwise, returns the corresponding parameter of the subscript
|
||||
// declaration.
|
||||
|
||||
@@ -1821,13 +1821,56 @@ static bool checkWitnessAccess(DeclContext *dc,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WitnessChecker::
|
||||
checkWitnessAvailability(ValueDecl *requirement,
|
||||
ValueDecl *witness,
|
||||
AvailabilityRange *requiredAvailability) {
|
||||
return (!getASTContext().LangOpts.DisableAvailabilityChecking &&
|
||||
!TypeChecker::isAvailabilitySafeForConformance(
|
||||
Proto, requirement, witness, DC, *requiredAvailability));
|
||||
static std::optional<AvailabilityConstraint>
|
||||
checkWitnessAvailability(const ValueDecl *requirement, const ValueDecl *witness,
|
||||
const DeclContext *dc,
|
||||
AvailabilityContext &requiredContext) {
|
||||
auto &ctx = dc->getASTContext();
|
||||
if (ctx.LangOpts.DisableAvailabilityChecking)
|
||||
return std::nullopt;
|
||||
|
||||
// If the requirement is self-witnessing then no need to check availability.
|
||||
if (requirement == witness)
|
||||
return std::nullopt;
|
||||
|
||||
// We assume conformances in implicit code have already been checked for
|
||||
// availability.
|
||||
if (!dc->getParentSourceFile())
|
||||
return std::nullopt;
|
||||
|
||||
assert(dc->getSelfNominalTypeDecl() &&
|
||||
"Must have a nominal or extension context");
|
||||
|
||||
auto requirementAvailability =
|
||||
AvailabilityContext::forDeclSignature(requirement);
|
||||
requiredContext.constrainWithContext(requirementAvailability, ctx);
|
||||
|
||||
// The witness is allowed to be less available than the requirement as long
|
||||
// as it is as available as the overall conformance.
|
||||
auto conformanceAvailability =
|
||||
AvailabilityContext::forDeclSignature(dc->getAsDecl());
|
||||
requiredContext.constrainWithContext(conformanceAvailability, ctx);
|
||||
|
||||
// Relax the requirements for @_spi witnesses by treating the requirement as
|
||||
// if it were introduced at the deployment target. This is not strictly sound
|
||||
// since clients of SPI do not necessarily have the same deployment target as
|
||||
// the module declaring the requirement. However, now that the public
|
||||
// declarations in API libraries are checked according to the minimum possible
|
||||
// deployment target of their clients this relaxation is needed for source
|
||||
// compatibility with some existing code and is reasonably safe for the
|
||||
// majority of cases.
|
||||
if (witness->isSPI())
|
||||
requiredContext.constrainWithContext(
|
||||
AvailabilityContext::forDeploymentTarget(ctx), ctx);
|
||||
|
||||
// In order to maintain source compatibility, universally unavailable decls
|
||||
// are allowed to witness universally unavailable requirements.
|
||||
AvailabilityConstraintFlags flags;
|
||||
flags |= AvailabilityConstraintFlag::
|
||||
AllowUniversallyUnavailableInCompatibleContexts;
|
||||
|
||||
return getAvailabilityConstraintsForDecl(witness, requiredContext, flags)
|
||||
.getPrimaryConstraint();
|
||||
}
|
||||
|
||||
RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
|
||||
@@ -1850,12 +1893,14 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
|
||||
return CheckKind::UsableFromInline;
|
||||
}
|
||||
|
||||
auto requiredAvailability = AvailabilityRange::alwaysAvailable();
|
||||
if (checkWitnessAvailability(requirement, match.Witness,
|
||||
&requiredAvailability)) {
|
||||
return RequirementCheck(requiredAvailability);
|
||||
}
|
||||
// A witness cannot be less available than its requirement.
|
||||
auto requiredContext = AvailabilityContext::forAlwaysAvailable(Context);
|
||||
if (auto constraint = checkWitnessAvailability(requirement, match.Witness, DC,
|
||||
requiredContext))
|
||||
return RequirementCheck(*constraint, requiredContext);
|
||||
|
||||
// An unavailable requirement cannot be witnessed, just like an unavailable
|
||||
// method cannot be overridden.
|
||||
if (requirement->isUnavailable() && match.Witness->getDeclContext() == DC) {
|
||||
return RequirementCheck(CheckKind::RequirementUnavailable);
|
||||
}
|
||||
@@ -1878,27 +1923,6 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
|
||||
}
|
||||
}
|
||||
|
||||
if (match.Witness->isUnavailable() && !requirement->isUnavailable()) {
|
||||
auto nominalOrExtensionIsUnavailable = [&]() {
|
||||
if (auto extension = dyn_cast<ExtensionDecl>(DC)) {
|
||||
if (extension->isUnavailable())
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auto adoptingNominal = DC->getSelfNominalTypeDecl()) {
|
||||
if (AvailabilityContext::forDeclSignature(adoptingNominal)
|
||||
.isUnavailable())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// Allow unavailable nominals or extension to have unavailable witnesses.
|
||||
if (!nominalOrExtensionIsUnavailable())
|
||||
return RequirementCheck(AvailabilityRange::neverAvailable());
|
||||
}
|
||||
|
||||
// Warn about deprecated default implementations if the requirement is
|
||||
// not deprecated, and the conformance is not deprecated.
|
||||
bool isDefaultWitness = false;
|
||||
@@ -4468,20 +4492,31 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
|
||||
|
||||
case CheckKind::Availability: {
|
||||
if (check.isLessAvailable()) {
|
||||
ASSERT(check.getRequiredAvailabilityRange().hasMinimumVersion());
|
||||
getASTContext().addDelayedConformanceDiag(
|
||||
Conformance, false,
|
||||
[witness, requirement,
|
||||
check](NormalProtocolConformance *conformance) {
|
||||
ASTContext &ctx = witness->getASTContext();
|
||||
auto &diags = ctx.Diags;
|
||||
SourceLoc diagLoc =
|
||||
getLocForDiagnosingWitness(conformance, witness);
|
||||
diags.diagnose(diagLoc,
|
||||
diag::availability_protocol_requires_version,
|
||||
conformance->getProtocol(), witness,
|
||||
ctx.getTargetAvailabilityDomain(),
|
||||
check.getRequiredAvailabilityRange());
|
||||
auto diagLoc = getLocForDiagnosingWitness(conformance, witness);
|
||||
auto attr = check.getAvailabilityConstraint().getAttr();
|
||||
auto domain = attr.getDomain();
|
||||
auto requiredRange =
|
||||
check.getRequiredAvailabilityContext().getAvailabilityRange(
|
||||
domain, ctx);
|
||||
if (requiredRange) {
|
||||
diags.diagnose(
|
||||
diagLoc, diag::availability_protocol_requires_version,
|
||||
conformance->getProtocol(), witness,
|
||||
domain.isPlatform() ? ctx.getTargetAvailabilityDomain()
|
||||
: domain,
|
||||
*requiredRange);
|
||||
} else {
|
||||
diags.diagnose(
|
||||
diagLoc,
|
||||
diag::availability_protocol_requirement_only_available_in,
|
||||
conformance->getProtocol(), witness, domain);
|
||||
}
|
||||
emitDeclaredHereIfNeeded(diags, diagLoc, witness);
|
||||
diags.diagnose(requirement,
|
||||
diag::availability_protocol_requirement_here);
|
||||
@@ -4489,12 +4524,12 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
|
||||
} else {
|
||||
getASTContext().addDelayedConformanceDiag(
|
||||
Conformance, true,
|
||||
[witness, requirement](NormalProtocolConformance *conformance) {
|
||||
[witness, requirement,
|
||||
check](NormalProtocolConformance *conformance) {
|
||||
auto &diags = witness->getASTContext().Diags;
|
||||
auto diagLoc = getLocForDiagnosingWitness(conformance, witness);
|
||||
// FIXME: [availability] Get the original constraint.
|
||||
auto attr = witness->getUnavailableAttr();
|
||||
EncodedDiagnosticMessage EncodedMessage(attr->getMessage());
|
||||
auto attr = check.getAvailabilityConstraint().getAttr();
|
||||
EncodedDiagnosticMessage EncodedMessage(attr.getMessage());
|
||||
diags.diagnose(diagLoc, diag::witness_unavailable, witness,
|
||||
conformance->getProtocol(),
|
||||
EncodedMessage.Message);
|
||||
@@ -5219,62 +5254,91 @@ static void diagnoseInvariantSelfRequirement(
|
||||
.warnUntilSwiftVersion(6);
|
||||
}
|
||||
|
||||
static bool diagnoseTypeWitnessAvailability(
|
||||
NormalProtocolConformance *conformance, const TypeDecl *witness,
|
||||
const AssociatedTypeDecl *assocType, const ExportContext &where) {
|
||||
static bool
|
||||
diagnoseTypeWitnessAvailability(NormalProtocolConformance *conformance,
|
||||
const TypeDecl *witness,
|
||||
const AssociatedTypeDecl *assocType) {
|
||||
auto dc = conformance->getDeclContext();
|
||||
auto &ctx = dc->getASTContext();
|
||||
if (ctx.LangOpts.DisableAvailabilityChecking)
|
||||
return false;
|
||||
|
||||
auto requiredAvailability = AvailabilityContext::forAlwaysAvailable(ctx);
|
||||
auto constraint =
|
||||
checkWitnessAvailability(assocType, witness, dc, requiredAvailability);
|
||||
if (!constraint)
|
||||
return false;
|
||||
|
||||
auto attr = constraint->getAttr();
|
||||
auto domain = attr.getDomain();
|
||||
|
||||
// In Swift 6 and earlier type witness availability diagnostics are warnings.
|
||||
using namespace version;
|
||||
const unsigned warnBeforeVersion = Version::getFutureMajorLanguageVersion();
|
||||
bool shouldError =
|
||||
ctx.LangOpts.EffectiveLanguageVersion.isVersionAtLeast(warnBeforeVersion);
|
||||
|
||||
auto constraint =
|
||||
getAvailabilityConstraintsForDecl(witness, where.getAvailability())
|
||||
.getPrimaryConstraint();
|
||||
if (constraint && constraint->isUnavailable()) {
|
||||
auto attr = constraint->getAttr();
|
||||
switch (domain.getKind()) {
|
||||
case AvailabilityDomain::Kind::Universal:
|
||||
case AvailabilityDomain::Kind::SwiftLanguage:
|
||||
case AvailabilityDomain::Kind::PackageDescription:
|
||||
case AvailabilityDomain::Kind::Platform:
|
||||
break;
|
||||
case AvailabilityDomain::Kind::Embedded:
|
||||
case AvailabilityDomain::Kind::Custom:
|
||||
shouldError = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (constraint->isUnavailable()) {
|
||||
ctx.addDelayedConformanceDiag(
|
||||
conformance, shouldError,
|
||||
[witness, assocType, attr](NormalProtocolConformance *conformance) {
|
||||
[witness, assocType, attr,
|
||||
shouldError](NormalProtocolConformance *conformance) {
|
||||
SourceLoc loc = getLocForDiagnosingWitness(conformance, witness);
|
||||
EncodedDiagnosticMessage encodedMessage(attr.getMessage());
|
||||
auto &ctx = conformance->getDeclContext()->getASTContext();
|
||||
ctx.Diags
|
||||
.diagnose(loc, diag::witness_unavailable, witness,
|
||||
conformance->getProtocol(), encodedMessage.Message)
|
||||
.warnUntilSwiftVersion(warnBeforeVersion);
|
||||
.warnUntilSwiftVersionIf(!shouldError, warnBeforeVersion);
|
||||
|
||||
emitDeclaredHereIfNeeded(ctx.Diags, loc, witness);
|
||||
ctx.Diags.diagnose(assocType, diag::requirement_declared_here,
|
||||
assocType);
|
||||
});
|
||||
}
|
||||
|
||||
auto requiredRange = AvailabilityRange::alwaysAvailable();
|
||||
if (!TypeChecker::isAvailabilitySafeForConformance(
|
||||
conformance->getProtocol(), assocType, witness, dc, requiredRange)) {
|
||||
ctx.addDelayedConformanceDiag(
|
||||
conformance, shouldError,
|
||||
[witness, requiredRange](NormalProtocolConformance *conformance) {
|
||||
SourceLoc loc = getLocForDiagnosingWitness(conformance, witness);
|
||||
auto &ctx = conformance->getDeclContext()->getASTContext();
|
||||
ctx.Diags
|
||||
.diagnose(loc, diag::availability_protocol_requires_version,
|
||||
conformance->getProtocol(), witness,
|
||||
ctx.getTargetAvailabilityDomain(), requiredRange)
|
||||
.warnUntilSwiftVersion(warnBeforeVersion);
|
||||
|
||||
emitDeclaredHereIfNeeded(ctx.Diags, loc, witness);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
auto requiredRange = requiredAvailability.getAvailabilityRange(domain, ctx);
|
||||
|
||||
ctx.addDelayedConformanceDiag(
|
||||
conformance, shouldError,
|
||||
[witness, attr, requiredRange,
|
||||
shouldError](NormalProtocolConformance *conformance) {
|
||||
SourceLoc loc = getLocForDiagnosingWitness(conformance, witness);
|
||||
auto &ctx = conformance->getDeclContext()->getASTContext();
|
||||
auto domain = attr.getDomain();
|
||||
if (requiredRange) {
|
||||
ctx.Diags
|
||||
.diagnose(loc, diag::availability_protocol_requires_version,
|
||||
conformance->getProtocol(), witness,
|
||||
domain.isPlatform() ? ctx.getTargetAvailabilityDomain()
|
||||
: domain,
|
||||
*requiredRange)
|
||||
.warnUntilSwiftVersionIf(!shouldError, warnBeforeVersion);
|
||||
} else {
|
||||
ctx.Diags
|
||||
.diagnose(
|
||||
loc,
|
||||
diag::availability_protocol_requirement_only_available_in,
|
||||
conformance->getProtocol(), witness, domain)
|
||||
.warnUntilSwiftVersionIf(!shouldError, warnBeforeVersion);
|
||||
}
|
||||
|
||||
emitDeclaredHereIfNeeded(ctx.Diags, loc, witness);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Check whether the type witnesses satisfy the protocol's requirement
|
||||
@@ -5407,7 +5471,7 @@ static void ensureRequirementsAreSatisfied(ASTContext &ctx,
|
||||
|
||||
// The type witness must be as available as the associated type.
|
||||
if (auto witness = type->getAnyNominal())
|
||||
diagnoseTypeWitnessAvailability(conformance, witness, assocType, where);
|
||||
diagnoseTypeWitnessAvailability(conformance, witness, assocType);
|
||||
|
||||
// Make sure any associated type witnesses don't make reference to a
|
||||
// type we can't emit metadata for, or we're going to have trouble at
|
||||
|
||||
@@ -88,10 +88,6 @@ protected:
|
||||
unsigned &bestIdx,
|
||||
bool &doNotDiagnoseMatches);
|
||||
|
||||
bool checkWitnessAvailability(ValueDecl *requirement,
|
||||
ValueDecl *witness,
|
||||
AvailabilityRange *requirementInfo);
|
||||
|
||||
RequirementCheck checkWitness(ValueDecl *requirement,
|
||||
const RequirementMatch &match);
|
||||
};
|
||||
|
||||
@@ -1025,15 +1025,6 @@ bool diagnoseConformanceExportability(SourceLoc loc,
|
||||
/// potentially unavailable API elements
|
||||
/// @{
|
||||
|
||||
/// Returns true if the availability of the witness
|
||||
/// is sufficient to safely conform to the requirement in the context
|
||||
/// the provided conformance. On return, requiredAvailability holds th
|
||||
/// availability levels required for conformance.
|
||||
bool isAvailabilitySafeForConformance(
|
||||
const ProtocolDecl *proto, const ValueDecl *requirement,
|
||||
const ValueDecl *witness, const DeclContext *dc,
|
||||
AvailabilityRange &requiredAvailability);
|
||||
|
||||
/// Returns a diagnostic indicating why the declaration cannot be annotated
|
||||
/// with an @available() attribute indicating it is potentially unavailable
|
||||
/// or None if this is allowed.
|
||||
|
||||
@@ -253,18 +253,117 @@ extension Container {
|
||||
func unavailableInEnabledDomain() { }
|
||||
}
|
||||
|
||||
protocol P { }
|
||||
protocol OpaqueReturnType { }
|
||||
|
||||
@available(EnabledDomain)
|
||||
struct AvailableConformsToP: P { }
|
||||
struct AvailableOpaqueReturnType: OpaqueReturnType { }
|
||||
|
||||
@available(EnabledDomain, unavailable)
|
||||
struct UnavailableConformsToP: P { }
|
||||
struct UnavailableOpaqueReturnType: OpaqueReturnType { }
|
||||
|
||||
func testOpaqueReturnType() -> some P {
|
||||
func testOpaqueReturnType() -> some OpaqueReturnType {
|
||||
if #available(EnabledDomain) { // expected-error {{opaque return type cannot depend on EnabledDomain availability}}
|
||||
return AvailableConformsToP()
|
||||
return AvailableOpaqueReturnType()
|
||||
} else {
|
||||
return UnavailableConformsToP()
|
||||
return UnavailableOpaqueReturnType()
|
||||
}
|
||||
}
|
||||
|
||||
protocol HasRequirementInEnabledDomain {
|
||||
func alwaysAvailableRequirement()
|
||||
// expected-note@-1 3 {{protocol requirement here}}
|
||||
// expected-note@-2 {{requirement 'alwaysAvailableRequirement()' declared here}}
|
||||
|
||||
@available(EnabledDomain)
|
||||
func availableInEnabledDomain()
|
||||
// expected-note@-1 2 {{protocol requirement here}}
|
||||
// expected-note@-2 {{requirement 'availableInEnabledDomain()' declared here}}
|
||||
}
|
||||
|
||||
protocol HasAssocTypeRequirementInEnabledDomain {
|
||||
associatedtype A // expected-note {{requirement 'A' declared here}}
|
||||
|
||||
@available(EnabledDomain)
|
||||
associatedtype B // expected-note {{requirement 'B' declared here}}
|
||||
}
|
||||
|
||||
protocol HasRequirementUnavailableInEnabledDomain {
|
||||
@available(EnabledDomain, unavailable) // expected-error {{protocol members can only be marked unavailable in an '@objc' protocol}}
|
||||
func unavailableInEnabledDomain()
|
||||
}
|
||||
|
||||
struct ConformsToHasRequirementsInEnabledDomain: HasRequirementInEnabledDomain {
|
||||
func alwaysAvailableRequirement() { }
|
||||
func availableInEnabledDomain() { }
|
||||
}
|
||||
|
||||
@available(EnabledDomain)
|
||||
struct ConformsToHasRequirementsInEnabledDomain1: HasRequirementInEnabledDomain {
|
||||
func alwaysAvailableRequirement() { }
|
||||
func availableInEnabledDomain() { }
|
||||
}
|
||||
|
||||
@available(EnabledDomain, unavailable)
|
||||
struct ConformsToHasRequirementsInEnabledDomain2: HasRequirementInEnabledDomain {
|
||||
func alwaysAvailableRequirement() { }
|
||||
func availableInEnabledDomain() { }
|
||||
}
|
||||
|
||||
struct ConformsToHasRequirementsInEnabledDomain3: HasRequirementInEnabledDomain {
|
||||
@available(EnabledDomain)
|
||||
func alwaysAvailableRequirement() { } // expected-error {{protocol 'HasRequirementInEnabledDomain' requirement 'alwaysAvailableRequirement()' cannot be satisfied by instance method that is only available in EnabledDomain}}
|
||||
@available(EnabledDomain)
|
||||
func availableInEnabledDomain() { }
|
||||
}
|
||||
|
||||
struct ConformsToHasRequirementsInEnabledDomain4: HasRequirementInEnabledDomain { // expected-error {{type 'ConformsToHasRequirementsInEnabledDomain4' does not conform to protocol 'HasRequirementInEnabledDomain'}}
|
||||
@available(EnabledDomain, unavailable)
|
||||
func alwaysAvailableRequirement() { } // expected-error {{unavailable instance method 'alwaysAvailableRequirement()' was used to satisfy a requirement of protocol 'HasRequirementInEnabledDomain'}}
|
||||
@available(EnabledDomain, unavailable)
|
||||
func availableInEnabledDomain() { } // expected-error {{unavailable instance method 'availableInEnabledDomain()' was used to satisfy a requirement of protocol 'HasRequirementInEnabledDomain'}}
|
||||
}
|
||||
|
||||
struct ConformsToHasRequirementsInEnabledDomain5: HasRequirementInEnabledDomain {
|
||||
@available(DisabledDomain)
|
||||
func alwaysAvailableRequirement() { } // expected-error {{protocol 'HasRequirementInEnabledDomain' requirement 'alwaysAvailableRequirement()' cannot be satisfied by instance method that is only available in DisabledDomain}}
|
||||
@available(DisabledDomain)
|
||||
func availableInEnabledDomain() { } // expected-error {{protocol 'HasRequirementInEnabledDomain' requirement 'availableInEnabledDomain()' cannot be satisfied by instance method that is only available in DisabledDomain}}
|
||||
}
|
||||
|
||||
struct ConformsToHasRequirementsInEnabledDomain6: HasRequirementInEnabledDomain {
|
||||
@available(EnabledDomain)
|
||||
@available(DisabledDomain)
|
||||
func alwaysAvailableRequirement() { } // expected-error {{protocol 'HasRequirementInEnabledDomain' requirement 'alwaysAvailableRequirement()' cannot be satisfied by instance method that is only available in DisabledDomain}}
|
||||
@available(EnabledDomain)
|
||||
@available(DisabledDomain)
|
||||
func availableInEnabledDomain() { } // expected-error {{protocol 'HasRequirementInEnabledDomain' requirement 'availableInEnabledDomain()' cannot be satisfied by instance method that is only available in DisabledDomain}}
|
||||
}
|
||||
|
||||
struct ConformsToHasAssocTypeRequirementInEnabledDomain: HasAssocTypeRequirementInEnabledDomain {
|
||||
struct A { }
|
||||
struct B { }
|
||||
}
|
||||
|
||||
struct ConformsToHasAssocTypeRequirementInEnabledDomain1: HasAssocTypeRequirementInEnabledDomain { // expected-error {{type 'ConformsToHasAssocTypeRequirementInEnabledDomain1' does not conform to protocol 'HasAssocTypeRequirementInEnabledDomain'}}
|
||||
@available(EnabledDomain)
|
||||
struct A { } // expected-error {{protocol 'HasAssocTypeRequirementInEnabledDomain' requirement 'A' cannot be satisfied by struct that is only available in EnabledDomain}}
|
||||
|
||||
@available(EnabledDomain)
|
||||
struct B { }
|
||||
}
|
||||
|
||||
struct ConformsToHasAssocTypeRequirementInEnabledDomain2: HasAssocTypeRequirementInEnabledDomain { // expected-error {{type 'ConformsToHasAssocTypeRequirementInEnabledDomain2' does not conform to protocol 'HasAssocTypeRequirementInEnabledDomain'}}
|
||||
@available(EnabledDomain, unavailable)
|
||||
struct A { } // expected-error {{unavailable struct 'A' was used to satisfy a requirement of protocol 'HasAssocTypeRequirementInEnabledDomain'}}
|
||||
|
||||
@available(EnabledDomain, unavailable)
|
||||
struct B { } // expected-error {{unavailable struct 'B' was used to satisfy a requirement of protocol 'HasAssocTypeRequirementInEnabledDomain'}}
|
||||
}
|
||||
|
||||
struct ConformsToHasAssocTypeRequirementInEnabledDomain3: HasAssocTypeRequirementInEnabledDomain { // expected-error {{type 'ConformsToHasAssocTypeRequirementInEnabledDomain3' does not conform to protocol 'HasAssocTypeRequirementInEnabledDomain'}}
|
||||
@available(DisabledDomain)
|
||||
struct A { } // expected-error {{protocol 'HasAssocTypeRequirementInEnabledDomain' requirement 'A' cannot be satisfied by struct that is only available in DisabledDomain}}
|
||||
|
||||
@available(DisabledDomain)
|
||||
struct B { } // expected-error {{protocol 'HasAssocTypeRequirementInEnabledDomain' requirement 'B' cannot be satisfied by struct that is only available in DisabledDomain}}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user