Skip to content

Commit 9bba346

Browse files
committed
Sema: Check conditional availability earlier in conformance checking
We would only check conditional availability once we had committed to a best witness. Instead, let's use this information to disambiguate among multiple best witnesses, if we have them. Access control and unconditional unavailability are still only checked once the witness is chosen. Fixes rdar://problem/77259046.
1 parent a615d2c commit 9bba346

File tree

3 files changed

+71
-10
lines changed

3 files changed

+71
-10
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1356,6 +1356,27 @@ bool WitnessChecker::findBestWitness(
13561356
}
13571357
}
13581358

1359+
// If there are multiple viable matches, drop any that are less available than the
1360+
// requirement.
1361+
if (numViable > 1) {
1362+
SmallVector<RequirementMatch, 2> checkedMatches;
1363+
bool foundCheckedMatch = false;
1364+
1365+
for (auto match : matches) {
1366+
if (!match.isViable()) {
1367+
checkedMatches.push_back(match);
1368+
} else if (checkWitness(requirement, match).Kind != CheckKind::Availability) {
1369+
foundCheckedMatch = true;
1370+
checkedMatches.push_back(match);
1371+
}
1372+
}
1373+
1374+
// If none of the matches were at least as available as the requirement, don't
1375+
// drop any of them; this will produce better diagnostics.
1376+
if (foundCheckedMatch)
1377+
std::swap(checkedMatches, matches);
1378+
}
1379+
13591380
if (numViable == 0) {
13601381
// Assume any missing value witnesses for a conformance in a module
13611382
// interface can be treated as opaque.

test/Sema/availability_versions.swift

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,7 +1385,7 @@ protocol ProtocolWithRequirementMentioningUnavailable {
13851385

13861386
protocol HasMethodF {
13871387
associatedtype T
1388-
func f(_ p: T) // expected-note 4{{protocol requirement here}}
1388+
func f(_ p: T) // expected-note 3{{protocol requirement here}}
13891389
}
13901390

13911391
class TriesToConformWithFunctionIntroducedOn10_51 : HasMethodF {
@@ -1499,17 +1499,14 @@ extension TakesClassAvailableOn10_51_B : HasTakesClassAvailableOn10_51 {
14991499
}
15001500

15011501

1502-
// We do not want potential unavailability to play a role in picking a witness for a
1503-
// protocol requirement. Rather, the witness should be chosen, regardless of its
1504-
// potential unavailability, and then it should be diagnosed if it is less available
1505-
// than the protocol requires.
1506-
class TestAvailabilityDoesNotAffectWitnessCandidacy : HasMethodF {
1507-
// Test that we choose the more specialized witness even though it is
1508-
// less available than the protocol requires and there is a less specialized
1509-
// witness that has suitable availability.
1502+
// We want conditional availability to play a role in picking a witness for a
1503+
// protocol requirement.
1504+
class TestAvailabilityAffectsWitnessCandidacy : HasMethodF {
1505+
// Test that we choose the less specialized witness, because the more specialized
1506+
// witness is conditionally unavailable.
15101507

15111508
@available(OSX, introduced: 10.51)
1512-
func f(_ p: Int) { } // expected-error {{protocol 'HasMethodF' requires 'f' to be available in macOS 10.50.0 and newer}}
1509+
func f(_ p: Int) { }
15131510

15141511
func f<T>(_ p: T) { }
15151512
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// RUN: %target-swift-frontend -emit-sil -verify %s | %FileCheck %s
2+
3+
// REQUIRES: OS=macosx
4+
5+
public protocol Potato {
6+
func eat()
7+
}
8+
9+
public protocol Fried {}
10+
11+
extension Potato {
12+
public func eat() {}
13+
}
14+
15+
@available(macOS 100, *)
16+
extension Potato where Self : Fried {
17+
public func eat() {}
18+
}
19+
20+
// We ought to pick the unconstrained Potato.eat(), not
21+
// the one from the extension, because the extension has
22+
// narrower availability than the conformance.
23+
public struct CurlyFries : Potato, Fried {}
24+
25+
public struct TaterTots {}
26+
27+
// This conformance on the other hand should use the
28+
// constrained Potato.eat(), since the generic signature
29+
// is more specific than the unconstrained protocol
30+
// extension.
31+
@available(macOS 100, *)
32+
extension TaterTots : Potato, Fried {}
33+
34+
// We FileCheck the SILGen output to verify that the correct
35+
// witnesses were chosen above.
36+
37+
// CHECK-LABEL: sil shared [transparent] [thunk] @$s37conformance_availability_disambiguate10CurlyFriesVAA6PotatoA2aDP3eatyyFTW : $@convention(witness_method: Potato) (@in_guaranteed CurlyFries) -> () {
38+
// CHECK: function_ref @$s37conformance_availability_disambiguate6PotatoPAAE3eatyyF : $@convention(method) <τ_0_0 where τ_0_0 : Potato> (@in_guaranteed τ_0_0) -> ()
39+
// CHECK: return
40+
41+
// sil shared [transparent] [thunk] @$s37conformance_availability_disambiguate9TaterTotsVAA6PotatoA2aDP3eatyyFTW : $@convention(witness_method: Potato) (@in_guaranteed TaterTots) -> () {
42+
// CHECK: function_ref @$s37conformance_availability_disambiguate6PotatoPA2A5FriedRzrlE3eatyyF : $@convention(method) <τ_0_0 where τ_0_0 : Fried, τ_0_0 : Potato> (@in_guaranteed τ_0_0) -> ()
43+
// CHECK: return

0 commit comments

Comments
 (0)