File tree Expand file tree Collapse file tree 6 files changed +92
-1
lines changed Expand file tree Collapse file tree 6 files changed +92
-1
lines changed Original file line number Diff line number Diff line change @@ -618,6 +618,10 @@ class alignas(1 << DeclContextAlignInBits) DeclContext {
618618 // / is also included.
619619 unsigned getSemanticDepth () const ;
620620
621+ // / Returns if this extension is always available on the current deployment
622+ // / target. Used for conformance lookup disambiguation.
623+ bool isAlwaysAvailableConformanceContext () const ;
624+
621625 // / \returns true if traversal was aborted, false otherwise.
622626 bool walkContext (ASTWalker &Walker);
623627
Original file line number Diff line number Diff line change @@ -529,6 +529,18 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances(
529529 ConformanceEntry *lhs,
530530 ConformanceEntry *rhs,
531531 bool &diagnoseSuperseded) {
532+ // If only one of the conformances is unconditionally available on the
533+ // current deployment target, pick that one.
534+ //
535+ // FIXME: Conformance lookup should really depend on source location for
536+ // this to be 100% correct.
537+ if (lhs->getDeclContext ()->isAlwaysAvailableConformanceContext () !=
538+ rhs->getDeclContext ()->isAlwaysAvailableConformanceContext ()) {
539+ return (lhs->getDeclContext ()->isAlwaysAvailableConformanceContext ()
540+ ? Ordering::Before
541+ : Ordering::After);
542+ }
543+
532544 // If one entry is fixed and the other is not, we have our answer.
533545 if (lhs->isFixed () != rhs->isFixed ()) {
534546 // If the non-fixed conformance is not replaceable, we have a failure to
Original file line number Diff line number Diff line change @@ -1305,3 +1305,22 @@ static bool isSpecializeExtensionContext(const DeclContext *dc) {
13051305bool DeclContext::isInSpecializeExtensionContext () const {
13061306 return isSpecializeExtensionContext (this );
13071307}
1308+
1309+ bool DeclContext::isAlwaysAvailableConformanceContext () const {
1310+ auto *ext = dyn_cast<ExtensionDecl>(this );
1311+ if (ext == nullptr )
1312+ return true ;
1313+
1314+ if (AvailableAttr::isUnavailable (ext))
1315+ return false ;
1316+
1317+ auto &ctx = getASTContext ();
1318+
1319+ AvailabilityContext conformanceAvailability{
1320+ AvailabilityInference::availableRange (ext, ctx)};
1321+
1322+ auto deploymentTarget =
1323+ AvailabilityContext::forDeploymentTarget (ctx);
1324+
1325+ return deploymentTarget.isContainedIn (conformanceAvailability);
1326+ }
Original file line number Diff line number Diff line change @@ -1066,7 +1066,27 @@ LookupConformanceInModuleRequest::evaluate(
10661066 }
10671067 }
10681068
1069- // FIXME: Ambiguity resolution.
1069+ assert (!conformances.empty ());
1070+
1071+ // If we have multiple conformances, first try to filter out any that are
1072+ // unavailable on the current deployment target.
1073+ //
1074+ // FIXME: Conformance lookup should really depend on source location for
1075+ // this to be 100% correct.
1076+ if (conformances.size () > 1 ) {
1077+ SmallVector<ProtocolConformance *, 2 > availableConformances;
1078+
1079+ for (auto *conformance : conformances) {
1080+ if (conformance->getDeclContext ()->isAlwaysAvailableConformanceContext ())
1081+ availableConformances.push_back (conformance);
1082+ }
1083+
1084+ // Don't filter anything out if all conformances are unavailable.
1085+ if (!availableConformances.empty ())
1086+ std::swap (availableConformances, conformances);
1087+ }
1088+
1089+ // If we still have multiple conformances, just pick the first one.
10701090 auto conformance = conformances.front ();
10711091
10721092 // Rebuild inherited conformances based on the root normal conformance.
Original file line number Diff line number Diff line change 1+ public protocol P { }
2+
3+ public struct HasUnavailableConformance { }
4+
5+ @available ( * , unavailable)
6+ extension HasUnavailableConformance : P { }
7+
8+ public struct HasConditionallyAvailableConformance { }
9+
10+ @available ( macOS 100 , * )
11+ extension HasConditionallyAvailableConformance : P { }
12+
13+ public struct HasAlwaysAvailableConformance { }
14+
15+ extension HasAlwaysAvailableConformance : P { }
Original file line number Diff line number Diff line change 1+ // RUN: %empty-directory(%t)
2+ // RUN: %target-swift-frontend -emit-module %S/Inputs/conformance_availability_overlapping_other.swift -emit-module-path %t/conformance_availability_overlapping_other.swiftmodule
3+ // RUN: %target-typecheck-verify-swift -I %t
4+
5+ // REQUIRES: OS=macosx
6+
7+ import conformance_availability_overlapping_other
8+
9+ extension HasUnavailableConformance : P { }
10+
11+ extension HasConditionallyAvailableConformance : P { }
12+
13+ extension HasAlwaysAvailableConformance : P { }
14+ // expected-warning@-1 {{conformance of 'HasAlwaysAvailableConformance' to protocol 'P' was already stated in the type's module 'conformance_availability_overlapping_other'}}
15+
16+ struct G < T : P > { }
17+
18+ // None of these should produce a warning about an unavailable conformance.
19+ func usesConformance( _: G < HasUnavailableConformance > ) { }
20+ func usesConformance( _: G < HasConditionallyAvailableConformance > ) { }
21+ func usesConformance( _: G < HasAlwaysAvailableConformance > ) { }
You can’t perform that action at this time.
0 commit comments