Skip to content

Commit 3343a6a

Browse files
authored
Merge pull request swiftlang#34671 from slavapestov/conformance-availability-solution-ranking
Sema: Check conformance availability when ranking solutions
2 parents 7abf272 + 0ff82ca commit 3343a6a

File tree

4 files changed

+111
-8
lines changed

4 files changed

+111
-8
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3134,10 +3134,16 @@ class ConstraintSystem {
31343134
});
31353135
}
31363136

3137-
/// Determine whether given declaration is unavailable in the current context.
3137+
/// Determine whether the given declaration is unavailable from the
3138+
/// current context.
31383139
bool isDeclUnavailable(const Decl *D,
31393140
ConstraintLocator *locator = nullptr) const;
31403141

3142+
/// Determine whether the given conformance is unavailable from the
3143+
/// current context.
3144+
bool isConformanceUnavailable(ProtocolConformanceRef conformance,
3145+
ConstraintLocator *locator = nullptr) const;
3146+
31413147
public:
31423148

31433149
/// Whether we should attempt to fix problems.

lib/Sema/CSSimplify.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5669,11 +5669,16 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
56695669
return SolutionKind::Unsolved;
56705670
}
56715671

5672+
auto *loc = getConstraintLocator(locator);
5673+
56725674
/// Record the given conformance as the result, adding any conditional
56735675
/// requirements if necessary.
56745676
auto recordConformance = [&](ProtocolConformanceRef conformance) {
56755677
// Record the conformance.
5676-
CheckedConformances.push_back({getConstraintLocator(locator), conformance});
5678+
CheckedConformances.push_back({loc, conformance});
5679+
5680+
if (isConformanceUnavailable(conformance, loc))
5681+
increaseScore(SK_Unavailable);
56775682

56785683
// This conformance may be conditional, in which case we need to consider
56795684
// those requirements as constraints too.
@@ -5721,7 +5726,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
57215726
auto protocolTy = protocol->getDeclaredInterfaceType();
57225727

57235728
// If this conformance has been fixed already, let's just consider this done.
5724-
if (isFixedRequirement(getConstraintLocator(locator), protocolTy))
5729+
if (isFixedRequirement(loc, protocolTy))
57255730
return SolutionKind::Solved;
57265731

57275732
// If this is a generic requirement let's try to record that
@@ -5768,7 +5773,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
57685773
auto dstType = getType(assignment->getDest());
57695774

57705775
auto *fix = IgnoreAssignmentDestinationType::create(
5771-
*this, srcType, dstType, getConstraintLocator(locator));
5776+
*this, srcType, dstType, loc);
57725777
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
57735778
}
57745779

@@ -5779,8 +5784,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
57795784
// let's record it as a "contextual mismatch" because diagnostic
57805785
// is going to be dependent on other contextual information.
57815786
if (path.back().is<LocatorPathElt::ContextualType>()) {
5782-
auto *fix = ContextualMismatch::create(*this, type, protocolTy,
5783-
getConstraintLocator(locator));
5787+
auto *fix = ContextualMismatch::create(*this, type, protocolTy, loc);
57845788
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
57855789
}
57865790

@@ -5818,7 +5822,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
58185822
// If this is an implicit Hashable conformance check generated for each
58195823
// index argument of the keypath subscript component, we could just treat
58205824
// it as though it conforms.
5821-
auto *loc = getConstraintLocator(locator);
58225825
if (loc->isResultOfKeyPathDynamicMemberLookup() ||
58235826
loc->isKeyPathSubscriptComponent()) {
58245827
if (protocol ==

lib/Sema/ConstraintSystem.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/AST/Initializer.h"
2323
#include "swift/AST/GenericEnvironment.h"
2424
#include "swift/AST/ParameterList.h"
25+
#include "swift/AST/ProtocolConformance.h"
2526
#include "swift/AST/TypeCheckRequests.h"
2627
#include "swift/Basic/Statistic.h"
2728
#include "swift/Sema/CSFix.h"
@@ -5088,6 +5089,37 @@ bool ConstraintSystem::isDeclUnavailable(const Decl *D,
50885089
return result.hasValue();
50895090
}
50905091

5092+
bool ConstraintSystem::isConformanceUnavailable(ProtocolConformanceRef conformance,
5093+
ConstraintLocator *locator) const {
5094+
if (!conformance.isConcrete())
5095+
return false;
5096+
5097+
auto *concrete = conformance.getConcrete();
5098+
auto *rootConf = concrete->getRootConformance();
5099+
auto *ext = dyn_cast<ExtensionDecl>(rootConf->getDeclContext());
5100+
if (ext == nullptr)
5101+
return false;
5102+
5103+
auto &ctx = getASTContext();
5104+
5105+
// First check whether this declaration is universally unavailable.
5106+
if (ext->getAttrs().isUnavailable(ctx))
5107+
return true;
5108+
5109+
SourceLoc loc;
5110+
5111+
if (locator) {
5112+
if (auto anchor = locator->getAnchor())
5113+
loc = getLoc(anchor);
5114+
}
5115+
5116+
// If not, let's check contextual unavailability.
5117+
ExportContext where = ExportContext::forFunctionBody(DC, loc);
5118+
auto result = TypeChecker::checkConformanceAvailability(
5119+
rootConf, ext, where);
5120+
return result.hasValue();
5121+
}
5122+
50915123
/// If we aren't certain that we've emitted a diagnostic, emit a fallback
50925124
/// diagnostic.
50935125
void ConstraintSystem::maybeProduceFallbackDiagnostic(

test/Sema/conformance_availability.swift

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,4 +259,66 @@ struct AssocConformanceAvailable4 {}
259259
@available(macOS 100, *)
260260
extension AssocConformanceAvailable4 : Rider {
261261
typealias H = HasAvailableConformance1
262-
}
262+
}
263+
264+
// Solution ranking should down-rank solutions involving unavailable conformances
265+
protocol First {}
266+
extension First {
267+
func doStuff<T>(_: T) -> Bool {}
268+
}
269+
270+
protocol Second {}
271+
extension Second {
272+
func doStuff(_: Int) -> Int {}
273+
}
274+
275+
struct ConformingType1 {}
276+
277+
extension ConformingType1 : First {}
278+
279+
@available(macOS 100, *)
280+
extension ConformingType1 : Second {}
281+
282+
func usesConformingType1(_ c: ConformingType1) {
283+
// We should pick First.doStuff() here, since Second.doStuff() is unavailable
284+
let result = c.doStuff(123)
285+
let _: Bool = result
286+
}
287+
288+
@available(macOS 100, *)
289+
func usesConformingType1a(_ c: ConformingType1) {
290+
// We should pick Second.doStuff() here, since it is more specialized than
291+
// First.doStuff()
292+
let result = c.doStuff(123)
293+
let _: Int = result
294+
}
295+
296+
// Same as above but unconditionally unavailable
297+
struct ConformingType2 {}
298+
299+
extension ConformingType2 : First {}
300+
301+
@available(*, unavailable)
302+
extension ConformingType2 : Second {}
303+
304+
func usesConformingType2(_ c: ConformingType2) {
305+
// We should pick First.doStuff() here, since Second.doStuff() is unavailable
306+
let result = c.doStuff(123)
307+
let _: Bool = result
308+
}
309+
310+
// Make sure this also works for synthesized conformances
311+
struct UnavailableHashable {
312+
let x: Int
313+
let y: Int
314+
}
315+
316+
@available(macOS 100, *)
317+
extension UnavailableHashable : Hashable {}
318+
319+
func usesUnavailableHashable(_ c: UnavailableHashable) {
320+
// expected-note@-1 2 {{add @available attribute to enclosing global function}}
321+
_ = Set([c])
322+
// expected-error@-1 2 {{conformance of 'UnavailableHashable' to 'Hashable' is only available in macOS 100 or newer}}
323+
// expected-note@-2 2 {{add 'if #available' version check}}
324+
}

0 commit comments

Comments
 (0)