Skip to content

Commit 0ff82ca

Browse files
committed
Sema: Check conformance availability when ranking solutions
This completes the work on <rdar://problem/35158274>, and also adds a test case for <rdar://problem/50627401>.
1 parent fe1c4a4 commit 0ff82ca

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
@@ -5612,11 +5612,16 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
56125612
return SolutionKind::Unsolved;
56135613
}
56145614

5615+
auto *loc = getConstraintLocator(locator);
5616+
56155617
/// Record the given conformance as the result, adding any conditional
56165618
/// requirements if necessary.
56175619
auto recordConformance = [&](ProtocolConformanceRef conformance) {
56185620
// Record the conformance.
5619-
CheckedConformances.push_back({getConstraintLocator(locator), conformance});
5621+
CheckedConformances.push_back({loc, conformance});
5622+
5623+
if (isConformanceUnavailable(conformance, loc))
5624+
increaseScore(SK_Unavailable);
56205625

56215626
// This conformance may be conditional, in which case we need to consider
56225627
// those requirements as constraints too.
@@ -5664,7 +5669,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
56645669
auto protocolTy = protocol->getDeclaredInterfaceType();
56655670

56665671
// If this conformance has been fixed already, let's just consider this done.
5667-
if (isFixedRequirement(getConstraintLocator(locator), protocolTy))
5672+
if (isFixedRequirement(loc, protocolTy))
56685673
return SolutionKind::Solved;
56695674

56705675
// If this is a generic requirement let's try to record that
@@ -5711,7 +5716,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
57115716
auto dstType = getType(assignment->getDest());
57125717

57135718
auto *fix = IgnoreAssignmentDestinationType::create(
5714-
*this, srcType, dstType, getConstraintLocator(locator));
5719+
*this, srcType, dstType, loc);
57155720
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
57165721
}
57175722

@@ -5722,8 +5727,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
57225727
// let's record it as a "contextual mismatch" because diagnostic
57235728
// is going to be dependent on other contextual information.
57245729
if (path.back().is<LocatorPathElt::ContextualType>()) {
5725-
auto *fix = ContextualMismatch::create(*this, type, protocolTy,
5726-
getConstraintLocator(locator));
5730+
auto *fix = ContextualMismatch::create(*this, type, protocolTy, loc);
57275731
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
57285732
}
57295733

@@ -5761,7 +5765,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
57615765
// If this is an implicit Hashable conformance check generated for each
57625766
// index argument of the keypath subscript component, we could just treat
57635767
// it as though it conforms.
5764-
auto *loc = getConstraintLocator(locator);
57655768
if (loc->isResultOfKeyPathDynamicMemberLookup() ||
57665769
loc->isKeyPathSubscriptComponent()) {
57675770
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)