[Custom availability] Fix conformance availability diagnostic

Emit a proper diagnostic for a conformance that is not available due to
custom availability that doesn't have version information, eliminating
an assertion.
This commit is contained in:
Doug Gregor
2025-11-03 22:25:54 -08:00
parent ba47e23f51
commit b0a565e458
4 changed files with 59 additions and 5 deletions

View File

@@ -7282,6 +7282,10 @@ ERROR(conformance_availability_only_version_newer, none,
"conformance of %0 to %1 is only available in %2 %3 or newer",
(Type, Type, AvailabilityDomain, AvailabilityRange))
ERROR(conformance_availability_not_available, none,
"conformance of %0 to %1 is only available in %2",
(Type, Type, AvailabilityDomain))
//------------------------------------------------------------------------------
// MARK: if #available(...)
//------------------------------------------------------------------------------

View File

@@ -1465,6 +1465,21 @@ static void configureAvailabilityDomains(const ASTContext &ctx,
for (auto dynamic : opts.AvailabilityDomains.DynamicDomains)
createAndInsertDomain(dynamic, CustomAvailabilityDomain::Kind::Dynamic);
// If we didn't see the UnicodeNormalization availability domain, set it
// appropriately.
if (domainMap.count(ctx.getIdentifier("UnicodeNormalization")) == 0) {
if (ctx.LangOpts.hasFeature(Feature::Embedded)) {
// Embedded Swift disables this domain by default.
createAndInsertDomain("UnicodeNormalization",
CustomAvailabilityDomain::Kind::Enabled);
} else {
// Non-Embedded Swift always enables the Unicode tables.
createAndInsertDomain("UnicodeNormalization",
CustomAvailabilityDomain::Kind::AlwaysEnabled);
}
}
mainModule->setAvailabilityDomains(std::move(domainMap));
}

View File

@@ -1041,15 +1041,22 @@ static bool diagnosePotentialUnavailability(
{
auto type = rootConf->getType();
auto proto = rootConf->getProtocol()->getDeclaredInterfaceType();
auto err = ctx.Diags.diagnose(
loc, diag::conformance_availability_only_version_newer, type, proto,
domain, availability);
auto err = availability.hasMinimumVersion()
? ctx.Diags.diagnose(
loc, diag::conformance_availability_only_version_newer, type, proto,
domain, availability)
: ctx.Diags.diagnose(
loc, diag::conformance_availability_not_available, type, proto,
domain);
auto behaviorLimit = behaviorLimitForExplicitUnavailability(rootConf, dc);
if (behaviorLimit >= DiagnosticBehavior::Warning)
if (!availability.hasMinimumVersion()) {
// Don't downgrade
} else if (behaviorLimit >= DiagnosticBehavior::Warning) {
err.limitBehavior(behaviorLimit);
else
} else {
err.warnUntilSwiftVersion(6);
}
// Direct a fixit to the error if an existing guard is nearly-correct
if (fixAvailabilityByNarrowingNearbyVersionCheck(loc, dc, domain,

View File

@@ -568,3 +568,31 @@ class DerivedUnavailable2: BaseAvailableInEnabledDomain { } // expected-error {{
@available(DisabledDomain, unavailable)
class DerivedUnavailable3: BaseAvailableInEnabledDomain { }
// Protocol conformance availability.
protocol P { }
struct MyType1 { }
@available(EnabledDomain)
extension MyType1: P { }
struct MyType2 { }
@available(AlwaysEnabledDomain)
extension MyType2: P { }
struct MyType3 { }
@available(DisabledDomain)
extension MyType3: P { }
func acceptP<T: P>(_: T.Type) { }
func testP() { // expected-note 2{{add '@available' attribute to enclosing global function}}
acceptP(MyType1.self) // expected-error{{conformance of 'MyType1' to 'P' is only available in EnabledDomain}}
// expected-note@-1{{add 'if #available' version check}}
acceptP(MyType2.self) // okay
acceptP(MyType3.self) // expected-error{{conformance of 'MyType3' to 'P' is only available in DisabledDomain}}
// expected-note@-1{{add 'if #available' version check}}
}