Skip to content

Commit 71753f3

Browse files
authored
[ConstraintSystem] Rank contextually unavailable overloads lower than other choices (swiftlang#29921)
Currently constraint solver is only capable of detecting universally unavailable overloads but that's insufficient because it's still possible to pick a contextually unavailable overload choice which could be better than e.g. generic overload, or one with defaulted arguments, marked as disfavored etc. Let's introduce `ConstraintSystem::isDeclUnavailable` which supports both universal and contextual unavailability and allow constraint solver to rank all unavailable overload choices lower than any other possible choice(s). Resolves: rdar://problem/59056638
1 parent 87b1920 commit 71753f3

File tree

6 files changed

+84
-19
lines changed

6 files changed

+84
-19
lines changed

lib/Sema/CSGen.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ namespace {
616616
if (!overloadType)
617617
continue;
618618

619-
if (!decl->getAttrs().isUnavailable(CS.getASTContext()) &&
619+
if (!CS.isDeclUnavailable(decl, constraint->getLocator()) &&
620620
!decl->getAttrs().hasAttribute<DisfavoredOverloadAttr>() &&
621621
isFavored(decl, overloadType)) {
622622
// If we might need to roll back the favored constraints, keep

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5765,7 +5765,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
57655765
auto argType = AnyFunctionType::composeInput(getASTContext(), args,
57665766
/*canonicalVarargs=*/false);
57675767
if (argType->isEqual(favoredType))
5768-
if (!decl->getAttrs().isUnavailable(getASTContext()))
5768+
if (!isDeclUnavailable(decl, memberLocator))
57695769
result.FavoredChoice = result.ViableCandidates.size();
57705770
}
57715771
}

lib/Sema/CSSolver.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2173,7 +2173,7 @@ void ConstraintSystem::partitionDisjunction(
21732173
if (!funcDecl)
21742174
return false;
21752175

2176-
if (!funcDecl->getAttrs().isUnavailable(getASTContext()))
2176+
if (!isDeclUnavailable(funcDecl, constraint->getLocator()))
21772177
return false;
21782178

21792179
unavailable.push_back(index);
@@ -2265,6 +2265,9 @@ Constraint *ConstraintSystem::selectDisjunction() {
22652265
}
22662266

22672267
bool DisjunctionChoice::attempt(ConstraintSystem &cs) const {
2268+
if (isUnavailable())
2269+
cs.increaseScore(SK_Unavailable);
2270+
22682271
cs.simplifyDisjunctionChoice(Choice);
22692272

22702273
if (ExplicitConversion)

lib/Sema/ConstraintSystem.cpp

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,7 @@ LookupResult &ConstraintSystem::lookupMember(Type base, DeclNameRef name) {
273273

274274
// If the entry we recorded was unavailable but this new entry is not,
275275
// replace the recorded entry with this one.
276-
auto &ctx = getASTContext();
277-
if (uniqueEntry->getAttrs().isUnavailable(ctx) &&
278-
!decl->getAttrs().isUnavailable(ctx)) {
276+
if (isDeclUnavailable(uniqueEntry) && !isDeclUnavailable(decl)) {
279277
uniqueEntry = decl;
280278
}
281279
}
@@ -1889,11 +1887,6 @@ isInvalidPartialApplication(ConstraintSystem &cs, const ValueDecl *member,
18891887
std::pair<Type, bool> ConstraintSystem::adjustTypeOfOverloadReference(
18901888
const OverloadChoice &choice, ConstraintLocator *locator,
18911889
Type boundType, Type refType) {
1892-
// If the declaration is unavailable, note that in the score.
1893-
if (choice.getDecl()->getAttrs().isUnavailable(getASTContext())) {
1894-
increaseScore(SK_Unavailable);
1895-
}
1896-
18971890
bool bindConstraintCreated = false;
18981891
const auto kind = choice.getKind();
18991892
if (kind != OverloadChoiceKind::DeclViaDynamic &&
@@ -3618,9 +3611,9 @@ void ConstraintSystem::generateConstraints(
36183611

36193612
if (favoredIndex) {
36203613
const auto &choice = choices[*favoredIndex];
3621-
assert((!choice.isDecl() ||
3622-
!choice.getDecl()->getAttrs().isUnavailable(getASTContext())) &&
3623-
"Cannot make unavailable decl favored!");
3614+
assert(
3615+
(!choice.isDecl() || !isDeclUnavailable(choice.getDecl(), locator)) &&
3616+
"Cannot make unavailable decl favored!");
36243617
recordChoice(constraints, *favoredIndex, choice, /*isFavored=*/true);
36253618
}
36263619

@@ -4290,3 +4283,26 @@ void ConstraintSystem::diagnoseFailureFor(SolutionApplicationTarget target) {
42904283
diag::failed_to_produce_diagnostic);
42914284
}
42924285
}
4286+
4287+
bool ConstraintSystem::isDeclUnavailable(const Decl *D,
4288+
ConstraintLocator *locator) const {
4289+
auto &ctx = getASTContext();
4290+
4291+
if (ctx.LangOpts.DisableAvailabilityChecking)
4292+
return false;
4293+
4294+
// First check whether this declaration is universally unavailable.
4295+
if (D->getAttrs().isUnavailable(ctx))
4296+
return true;
4297+
4298+
SourceLoc loc;
4299+
4300+
if (locator) {
4301+
if (auto *anchor = locator->getAnchor())
4302+
loc = anchor->getLoc();
4303+
}
4304+
4305+
// If not, let's check contextual unavailability.
4306+
AvailabilityContext result = AvailabilityContext::alwaysAvailable();
4307+
return !TypeChecker::isDeclAvailable(D, loc, DC, result);
4308+
}

lib/Sema/ConstraintSystem.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2476,6 +2476,10 @@ class ConstraintSystem {
24762476
});
24772477
}
24782478

2479+
/// Determine whether given declaration is unavailable in the current context.
2480+
bool isDeclUnavailable(const Decl *D,
2481+
ConstraintLocator *locator = nullptr) const;
2482+
24792483
public:
24802484

24812485
/// Whether we should attempt to fix problems.
@@ -4669,15 +4673,17 @@ Type isRawRepresentable(ConstraintSystem &cs, Type type,
46694673
KnownProtocolKind rawRepresentableProtocol);
46704674

46714675
class DisjunctionChoice {
4676+
ConstraintSystem &CS;
46724677
unsigned Index;
46734678
Constraint *Choice;
46744679
bool ExplicitConversion;
46754680
bool IsBeginningOfPartition;
46764681

46774682
public:
4678-
DisjunctionChoice(unsigned index, Constraint *choice, bool explicitConversion,
4679-
bool isBeginningOfPartition)
4680-
: Index(index), Choice(choice), ExplicitConversion(explicitConversion),
4683+
DisjunctionChoice(ConstraintSystem &cs, unsigned index, Constraint *choice,
4684+
bool explicitConversion, bool isBeginningOfPartition)
4685+
: CS(cs), Index(index), Choice(choice),
4686+
ExplicitConversion(explicitConversion),
46814687
IsBeginningOfPartition(isBeginningOfPartition) {}
46824688

46834689
unsigned getIndex() const { return Index; }
@@ -4692,7 +4698,7 @@ class DisjunctionChoice {
46924698

46934699
bool isUnavailable() const {
46944700
if (auto *decl = getDecl(Choice))
4695-
return decl->getAttrs().isUnavailable(decl->getASTContext());
4701+
return CS.isDeclUnavailable(decl, Choice->getLocator());
46964702
return false;
46974703
}
46984704

@@ -4899,7 +4905,7 @@ class DisjunctionChoiceProducer : public BindingProducer<DisjunctionChoice> {
48994905

49004906
++Index;
49014907

4902-
return DisjunctionChoice(currIndex, Choices[Ordering[currIndex]],
4908+
return DisjunctionChoice(CS, currIndex, Choices[Ordering[currIndex]],
49034909
IsExplicitConversion, isBeginningOfPartition);
49044910
}
49054911
};
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %target-swift-frontend -emit-sil -verify %s | %FileCheck %s
2+
3+
// REQUIRES: OS=macosx
4+
5+
protocol View {}
6+
7+
extension View {
8+
@_disfavoredOverload
9+
func frame(width: Double?, height: Double? = nil) {
10+
}
11+
}
12+
13+
@available(macOS 999, *)
14+
extension View {
15+
func frame(width: Double?) {
16+
}
17+
}
18+
19+
func test_disfavored_vs_unavailable(_ view: View) {
20+
view.frame(width: 100) // Ok
21+
// CHECK: function_ref @$s29availability_with_overloading4ViewPAAE5frame5width6heightySdSg_AGtF
22+
}
23+
24+
struct S {
25+
func foo<T: StringProtocol>(_: T) {}
26+
func bar(_: Int, _: Int = 0) {}
27+
}
28+
29+
@available(macOS 999, *)
30+
extension S {
31+
func foo(_: String) {}
32+
func bar(_: Int) {}
33+
}
34+
35+
func test_generic_vs_unavailable(_ s: S) {
36+
s.foo("") // Ok (picks available generic overload)
37+
// CHECK: function_ref @$s29availability_with_overloading1SV3fooyyxSyRzlF
38+
s.bar(42) // Ok (picks overload with defaulted argument)
39+
// CHECK: function_ref @$s29availability_with_overloading1SV3baryySi_SitF
40+
}

0 commit comments

Comments
 (0)