Skip to content

Sema: Relax associated conformance availability checking #76053

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,13 @@ ExportContext ExportContext::withExported(bool exported) const {
return copy;
}

ExportContext ExportContext::withRefinedAvailability(
const AvailabilityContext &availability) const {
auto copy = *this;
copy.RunningOSVersion.intersectWith(availability);
return copy;
}

std::optional<PlatformKind> ExportContext::getUnavailablePlatformKind() const {
if (Unavailable)
return PlatformKind(Platform);
Expand Down
5 changes: 5 additions & 0 deletions lib/Sema/TypeCheckAvailability.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ class ExportContext {
/// That is, this will perform a 'bitwise and' on the 'exported' bit.
ExportContext withExported(bool exported) const;

/// Produce a new context with the same properties as this one, except the
/// availability context is constrained by \p availability if necessary.
ExportContext
withRefinedAvailability(const AvailabilityContext &availability) const;

DeclContext *getDeclContext() const { return DC; }

AvailabilityContext getAvailabilityContext() const {
Expand Down
18 changes: 15 additions & 3 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4987,9 +4987,21 @@ static void ensureRequirementsAreSatisfied(ASTContext &ctx,
if (assocConf.isConcrete()) {
auto *concrete = assocConf.getConcrete();
auto replacementTy = dc->mapTypeIntoContext(concrete->getType());
diagnoseConformanceAvailability(conformance->getLoc(),
assocConf, where,
depTy, replacementTy);

// If this requirement has a dependent member type, only require the
// associated conformance to be as available as the requirement's
// associated type.
auto availability = AvailabilityContext::alwaysAvailable();
if (auto depMemberType = depTy->getAs<DependentMemberType>()) {
auto assocType = depMemberType->getAssocType();
availability.intersectWith(
TypeChecker::overApproximateAvailabilityAtLocation(
assocType->getLoc(), assocType->getDeclContext()));
}

diagnoseConformanceAvailability(
conformance->getLoc(), assocConf,
where.withRefinedAvailability(availability), depTy, replacementTy);
}

return false;
Expand Down
61 changes: 61 additions & 0 deletions test/Sema/conformance_availability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,15 @@ func passAvailableConformance1a(x: HasAvailableConformance1) {
_ = UsesHorse<HasAvailableConformance1>.self
}

// Always available conformance
struct HasAvailableConformance2: Horse {}

// Conformance available in macOS 200
struct HasAvailableConformance3 {}

@available(macOS 200, *)
extension HasAvailableConformance3: Horse {}

// Associated conformance with unavailability
protocol Rider {
associatedtype H : Horse
Expand Down Expand Up @@ -326,6 +335,58 @@ extension AssocConformanceAvailable4 : Rider {
typealias H = HasAvailableConformance1
}

@available(macOS 100, *)
protocol Saddle {
associatedtype H : Horse = HasAvailableConformance1
}

struct ConformsToSaddle1 : Saddle {}

struct ConformsToSaddle2 : Saddle {
typealias H = HasAvailableConformance2
}

struct ConformsToSaddle3 : Saddle {
// expected-error@-1 {{conformance of 'HasAvailableConformance3' to 'Horse' is only available in macOS 200 or newer}}
// expected-note@-2 {{add @available attribute to enclosing struct}}
// expected-note@-3 {{in associated type 'Self.H' (inferred as 'HasAvailableConformance3'}}
typealias H = HasAvailableConformance3
}

struct ConformsToSaddle4 : Saddle {
// expected-error@-1 {{conformance of 'HasAvailableConformance3' to 'Horse' is only available in macOS 200 or newer}}
// expected-note@-2 {{add @available attribute to enclosing struct}}
// expected-note@-3 {{in associated type 'Self.H' (inferred as 'HasAvailableConformance3'}}
@available(macOS 200, *)
typealias H = HasAvailableConformance3
}

protocol Barn {
@available(macOS 100, *)
associatedtype H : Horse = HasAvailableConformance1
}

struct ConformsToBarn1 : Barn {}

struct ConformsToBarn2 : Barn {
typealias H = HasAvailableConformance2
}

struct ConformsToBarn3 : Barn {
// expected-error@-1 {{conformance of 'HasAvailableConformance3' to 'Horse' is only available in macOS 200 or newer}}
// expected-note@-2 {{add @available attribute to enclosing struct}}
// expected-note@-3 {{in associated type 'Self.H' (inferred as 'HasAvailableConformance3'}}
typealias H = HasAvailableConformance3
}

struct ConformsToBarn4 : Barn {
// expected-error@-1 {{conformance of 'HasAvailableConformance3' to 'Horse' is only available in macOS 200 or newer}}
// expected-note@-2 {{add @available attribute to enclosing struct}}
// expected-note@-3 {{in associated type 'Self.H' (inferred as 'HasAvailableConformance3'}}
@available(macOS 200, *)
typealias H = HasAvailableConformance3
}

// Solution ranking should down-rank solutions involving unavailable conformances
protocol First {}
extension First {
Expand Down