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 {
618
618
// / is also included.
619
619
unsigned getSemanticDepth () const ;
620
620
621
+ // / Returns if this extension is always available on the current deployment
622
+ // / target. Used for conformance lookup disambiguation.
623
+ bool isAlwaysAvailableConformanceContext () const ;
624
+
621
625
// / \returns true if traversal was aborted, false otherwise.
622
626
bool walkContext (ASTWalker &Walker);
623
627
Original file line number Diff line number Diff line change @@ -529,6 +529,18 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances(
529
529
ConformanceEntry *lhs,
530
530
ConformanceEntry *rhs,
531
531
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
+
532
544
// If one entry is fixed and the other is not, we have our answer.
533
545
if (lhs->isFixed () != rhs->isFixed ()) {
534
546
// 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) {
1305
1305
bool DeclContext::isInSpecializeExtensionContext () const {
1306
1306
return isSpecializeExtensionContext (this );
1307
1307
}
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(
1066
1066
}
1067
1067
}
1068
1068
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.
1070
1090
auto conformance = conformances.front ();
1071
1091
1072
1092
// 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