Skip to content

Commit 8cfb7c2

Browse files
committed
Merge remote-tracking branch 'origin/master' into master-next
2 parents fde3510 + 3496078 commit 8cfb7c2

File tree

10 files changed

+240
-92
lines changed

10 files changed

+240
-92
lines changed

lib/Sema/CSApply.cpp

Lines changed: 54 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,15 +1561,6 @@ namespace {
15611561
LocatorPathElt::getKeyPathDynamicMember(keyPathTy->getAnyNominal()));
15621562
auto overload = solution.getOverloadChoice(componentLoc);
15631563

1564-
auto buildSubscriptComponent = [&](SourceLoc loc, Expr *indexExpr,
1565-
ArrayRef<Identifier> labels) {
1566-
// Save a reference to the component so we can do a post-pass to check
1567-
// the Hashable conformance of the indexes.
1568-
KeyPathSubscriptComponents.push_back({keyPath, 0});
1569-
return buildKeyPathSubscriptComponent(overload, loc, indexExpr, labels,
1570-
componentLoc);
1571-
};
1572-
15731564
auto getKeyPathComponentIndex =
15741565
[](ConstraintLocator *locator) -> unsigned {
15751566
for (const auto &elt : locator->getPath()) {
@@ -1583,9 +1574,10 @@ namespace {
15831574
// calls necessary to resolve a member reference.
15841575
if (overload.choice.getKind() ==
15851576
OverloadChoiceKind::KeyPathDynamicMemberLookup) {
1586-
keyPath->resolveComponents(
1587-
ctx, buildSubscriptComponent(dotLoc, /*indexExpr=*/nullptr,
1588-
ctx.Id_dynamicMember));
1577+
keyPath->resolveComponents(ctx,
1578+
buildKeyPathSubscriptComponent(
1579+
overload, dotLoc, /*indexExpr=*/nullptr,
1580+
ctx.Id_dynamicMember, componentLoc));
15891581
return keyPath;
15901582
}
15911583

@@ -1663,8 +1655,9 @@ namespace {
16631655
/*implicit=*/true, SE->getAccessSemantics());
16641656
}
16651657

1666-
component = buildSubscriptComponent(SE->getLoc(), SE->getIndex(),
1667-
SE->getArgumentLabels());
1658+
component = buildKeyPathSubscriptComponent(
1659+
overload, SE->getLoc(), SE->getIndex(), SE->getArgumentLabels(),
1660+
componentLoc);
16681661
} else {
16691662
return nullptr;
16701663
}
@@ -4296,12 +4289,7 @@ namespace {
42964289
E->setMethod(method);
42974290
return E;
42984291
}
4299-
4300-
private:
4301-
// Key path components we need to
4302-
SmallVector<std::pair<KeyPathExpr *, unsigned>, 4>
4303-
KeyPathSubscriptComponents;
4304-
public:
4292+
43054293
Expr *visitKeyPathExpr(KeyPathExpr *E) {
43064294
if (E->isObjC()) {
43074295
cs.setType(E, cs.getType(E->getObjCStringLiteralExpr()));
@@ -4443,10 +4431,6 @@ namespace {
44434431
*foundDecl, origComponent.getLoc(), origComponent.getIndexExpr(),
44444432
subscriptLabels, locator);
44454433

4446-
// Save a reference to the component so we can do a post-pass to check
4447-
// the Hashable conformance of the indexes.
4448-
KeyPathSubscriptComponents.push_back({E, resolvedComponents.size()});
4449-
44504434
baseTy = component.getComponentType();
44514435
resolvedComponents.push_back(component);
44524436

@@ -4622,10 +4606,13 @@ namespace {
46224606
// through the subscript(dynamicMember:) member, restore the
46234607
// openedType and origComponent to its full reference as if the user
46244608
// wrote out the subscript manually.
4625-
if (overload.choice.getKind() ==
4609+
bool forDynamicLookup =
4610+
overload.choice.getKind() ==
46264611
OverloadChoiceKind::DynamicMemberLookup ||
46274612
overload.choice.getKind() ==
4628-
OverloadChoiceKind::KeyPathDynamicMemberLookup) {
4613+
OverloadChoiceKind::KeyPathDynamicMemberLookup;
4614+
4615+
if (forDynamicLookup) {
46294616
overload.openedType =
46304617
overload.openedFullType->castTo<AnyFunctionType>()->getResult();
46314618

@@ -4654,8 +4641,48 @@ namespace {
46544641
/*applyExpr*/ nullptr, labels,
46554642
/*hasTrailingClosure*/ false, locator);
46564643

4657-
return KeyPathExpr::Component::forSubscriptWithPrebuiltIndexExpr(
4644+
auto component = KeyPathExpr::Component::forSubscriptWithPrebuiltIndexExpr(
46584645
ref, newIndexExpr, labels, resolvedTy, componentLoc, {});
4646+
4647+
// We need to be able to hash the captured index values in order for
4648+
// KeyPath itself to be hashable, so check that all of the subscript
4649+
// index components are hashable and collect their conformances here.
4650+
SmallVector<ProtocolConformanceRef, 4> conformances;
4651+
4652+
auto hashable =
4653+
cs.getASTContext().getProtocol(KnownProtocolKind::Hashable);
4654+
4655+
auto equatable =
4656+
cs.getASTContext().getProtocol(KnownProtocolKind::Equatable);
4657+
4658+
auto &TC = cs.getTypeChecker();
4659+
auto fnType = overload.openedType->castTo<FunctionType>();
4660+
for (const auto &param : fnType->getParams()) {
4661+
auto indexType = simplifyType(param.getPlainType());
4662+
// Index type conformance to Hashable protocol has been
4663+
// verified by the solver, we just need to get it again
4664+
// with all of the generic parameters resolved.
4665+
auto hashableConformance =
4666+
TC.conformsToProtocol(indexType, hashable, cs.DC,
4667+
(ConformanceCheckFlags::Used |
4668+
ConformanceCheckFlags::InExpression));
4669+
assert(hashableConformance.hasValue());
4670+
4671+
// FIXME: Hashable implies Equatable, but we need to make sure the
4672+
// Equatable conformance is forced into existence during type
4673+
// checking so that it's available for SILGen.
4674+
auto eqConformance =
4675+
TC.conformsToProtocol(indexType, equatable, cs.DC,
4676+
(ConformanceCheckFlags::Used |
4677+
ConformanceCheckFlags::InExpression));
4678+
assert(eqConformance.hasValue());
4679+
(void)eqConformance;
4680+
4681+
conformances.push_back(*hashableConformance);
4682+
}
4683+
4684+
component.setSubscriptIndexHashableConformances(conformances);
4685+
return component;
46594686
}
46604687

46614688
Expr *visitKeyPathDotExpr(KeyPathDotExpr *E) {
@@ -4724,61 +4751,6 @@ namespace {
47244751
.fixItInsertAfter(cast->getEndLoc(), ")");
47254752
}
47264753

4727-
// Look at key path subscript components to verify that they're hashable.
4728-
for (auto componentRef : KeyPathSubscriptComponents) {
4729-
auto &component = componentRef.first
4730-
->getMutableComponents()[componentRef.second];
4731-
// We need to be able to hash the captured index values in order for
4732-
// KeyPath itself to be hashable, so check that all of the subscript
4733-
// index components are hashable and collect their conformances here.
4734-
SmallVector<ProtocolConformanceRef, 2> hashables;
4735-
bool allIndexesHashable = true;
4736-
ArrayRef<TupleTypeElt> indexTypes;
4737-
TupleTypeElt singleIndexTypeBuf;
4738-
if (auto tup = cs.getType(component.getIndexExpr())
4739-
->getAs<TupleType>()) {
4740-
indexTypes = tup->getElements();
4741-
} else {
4742-
singleIndexTypeBuf = cs.getType(component.getIndexExpr());
4743-
indexTypes = singleIndexTypeBuf;
4744-
}
4745-
4746-
auto hashable =
4747-
cs.getASTContext().getProtocol(KnownProtocolKind::Hashable);
4748-
auto equatable =
4749-
cs.getASTContext().getProtocol(KnownProtocolKind::Equatable);
4750-
for (auto indexType : indexTypes) {
4751-
auto conformance =
4752-
cs.TC.conformsToProtocol(indexType.getType(), hashable,
4753-
cs.DC,
4754-
(ConformanceCheckFlags::Used|
4755-
ConformanceCheckFlags::InExpression));
4756-
if (!conformance) {
4757-
cs.TC.diagnose(component.getIndexExpr()->getLoc(),
4758-
diag::expr_keypath_subscript_index_not_hashable,
4759-
indexType.getType());
4760-
allIndexesHashable = false;
4761-
continue;
4762-
}
4763-
hashables.push_back(*conformance);
4764-
4765-
// FIXME: Hashable implies Equatable, but we need to make sure the
4766-
// Equatable conformance is forced into existence during type checking
4767-
// so that it's available for SILGen.
4768-
auto eqConformance =
4769-
cs.TC.conformsToProtocol(indexType.getType(), equatable,
4770-
cs.DC,
4771-
(ConformanceCheckFlags::Used|
4772-
ConformanceCheckFlags::InExpression));
4773-
assert(eqConformance.hasValue());
4774-
(void)eqConformance;
4775-
}
4776-
4777-
if (allIndexesHashable) {
4778-
component.setSubscriptIndexHashableConformances(hashables);
4779-
}
4780-
}
4781-
47824754
// Set the final types on the expression.
47834755
cs.setExprTypes(result);
47844756
}

lib/Sema/CSDiagnostics.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2398,3 +2398,23 @@ bool InaccessibleMemberFailure::diagnoseAsError() {
23982398
emitDiagnostic(Member, diag::decl_declared_here, Member->getFullName());
23992399
return true;
24002400
}
2401+
2402+
bool KeyPathSubscriptIndexHashableFailure::diagnoseAsError() {
2403+
auto *anchor = getRawAnchor();
2404+
auto *locator = getLocator();
2405+
2406+
auto loc = anchor->getLoc();
2407+
if (locator->isKeyPathSubscriptComponent()) {
2408+
auto *KPE = cast<KeyPathExpr>(anchor);
2409+
for (auto &elt : locator->getPath()) {
2410+
if (elt.isKeyPathComponent()) {
2411+
loc = KPE->getComponents()[elt.getValue()].getLoc();
2412+
break;
2413+
}
2414+
}
2415+
}
2416+
2417+
emitDiagnostic(loc, diag::expr_keypath_subscript_index_not_hashable,
2418+
resolveType(NonConformingType));
2419+
return true;
2420+
}

lib/Sema/CSDiagnostics.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,34 @@ class InaccessibleMemberFailure final : public FailureDiagnostic {
987987
bool diagnoseAsError() override;
988988
};
989989

990+
/// Diagnose an attempt to reference subscript as a keypath component
991+
/// where at least one of the index arguments doesn't conform to Hashable e.g.
992+
///
993+
/// ```swift
994+
/// protocol P {}
995+
///
996+
/// struct S {
997+
/// subscript<T: P>(x: Int, _ y: T) -> Bool { return true }
998+
/// }
999+
///
1000+
/// func foo<T: P>(_ x: Int, _ y: T) {
1001+
/// _ = \S.[x, y]
1002+
/// }
1003+
/// ```
1004+
class KeyPathSubscriptIndexHashableFailure final : public FailureDiagnostic {
1005+
Type NonConformingType;
1006+
1007+
public:
1008+
KeyPathSubscriptIndexHashableFailure(Expr *root, ConstraintSystem &cs,
1009+
Type type, ConstraintLocator *locator)
1010+
: FailureDiagnostic(root, cs, locator), NonConformingType(type) {
1011+
assert(locator->isResultOfKeyPathDynamicMemberLookup() ||
1012+
locator->isKeyPathSubscriptComponent());
1013+
}
1014+
1015+
bool diagnoseAsError() override;
1016+
};
1017+
9901018
} // end namespace constraints
9911019
} // end namespace swift
9921020

lib/Sema/CSFix.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,3 +396,17 @@ AllowInaccessibleMember::create(ConstraintSystem &cs, ValueDecl *member,
396396
ConstraintLocator *locator) {
397397
return new (cs.getAllocator()) AllowInaccessibleMember(cs, member, locator);
398398
}
399+
400+
bool TreatKeyPathSubscriptIndexAsHashable::diagnose(Expr *root,
401+
bool asNote) const {
402+
KeyPathSubscriptIndexHashableFailure failure(root, getConstraintSystem(),
403+
NonConformingType, getLocator());
404+
return failure.diagnose(asNote);
405+
}
406+
407+
TreatKeyPathSubscriptIndexAsHashable *
408+
TreatKeyPathSubscriptIndexAsHashable::create(ConstraintSystem &cs, Type type,
409+
ConstraintLocator *locator) {
410+
return new (cs.getAllocator())
411+
TreatKeyPathSubscriptIndexAsHashable(cs, type, locator);
412+
}

lib/Sema/CSFix.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ enum class FixKind : uint8_t {
136136
/// If there is a matching inaccessible member - allow it as if there
137137
/// no access control.
138138
AllowInaccessibleMember,
139+
140+
/// Using subscript references in the keypath requires that each
141+
// of the index arguments to be Hashable.
142+
TreatKeyPathSubscriptIndexAsHashable,
139143
};
140144

141145
class ConstraintFix {
@@ -737,6 +741,26 @@ class AllowInaccessibleMember final : public ConstraintFix {
737741
ConstraintLocator *locator);
738742
};
739743

744+
class TreatKeyPathSubscriptIndexAsHashable final : public ConstraintFix {
745+
Type NonConformingType;
746+
747+
TreatKeyPathSubscriptIndexAsHashable(ConstraintSystem &cs, Type type,
748+
ConstraintLocator *locator)
749+
: ConstraintFix(cs, FixKind::TreatKeyPathSubscriptIndexAsHashable,
750+
locator),
751+
NonConformingType(type) {}
752+
753+
public:
754+
std::string getName() const override {
755+
return "treat keypath subscript index as conforming to Hashable";
756+
}
757+
758+
bool diagnose(Expr *root, bool asNote = false) const override;
759+
760+
static TreatKeyPathSubscriptIndexAsHashable *
761+
create(ConstraintSystem &cs, Type type, ConstraintLocator *locator);
762+
};
763+
740764
} // end namespace constraints
741765
} // end namespace swift
742766

lib/Sema/CSSimplify.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3136,8 +3136,23 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
31363136
if (!recordFix(fix))
31373137
return SolutionKind::Solved;
31383138
}
3139+
3140+
// If this is an implicit Hashable conformance check generated for each
3141+
// index argument of the keypath subscript component, we could just treat
3142+
// it as though it conforms.
3143+
auto *loc = getConstraintLocator(locator);
3144+
if (loc->isResultOfKeyPathDynamicMemberLookup() ||
3145+
loc->isKeyPathSubscriptComponent()) {
3146+
if (protocol ==
3147+
getASTContext().getProtocol(KnownProtocolKind::Hashable)) {
3148+
auto *fix =
3149+
TreatKeyPathSubscriptIndexAsHashable::create(*this, type, loc);
3150+
if (!recordFix(fix))
3151+
return SolutionKind::Solved;
3152+
}
3153+
}
31393154
}
3140-
3155+
31413156
// There's nothing more we can do; fail.
31423157
return SolutionKind::Error;
31433158
}
@@ -6272,6 +6287,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
62726287
case FixKind::AllowClosureParameterDestructuring:
62736288
case FixKind::MoveOutOfOrderArgument:
62746289
case FixKind::AllowInaccessibleMember:
6290+
case FixKind::TreatKeyPathSubscriptIndexAsHashable:
62756291
llvm_unreachable("handled elsewhere");
62766292
}
62776293

lib/Sema/ConstraintLocator.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,30 @@ bool ConstraintLocator::isSubscriptMemberRef() const {
101101
return path.back().getKind() == ConstraintLocator::SubscriptMember;
102102
}
103103

104+
bool ConstraintLocator::isResultOfKeyPathDynamicMemberLookup() const {
105+
return llvm::any_of(getPath(), [](const LocatorPathElt &elt) {
106+
return elt.isKeyPathDynamicMember();
107+
});
108+
}
109+
110+
bool ConstraintLocator::isKeyPathSubscriptComponent() const {
111+
auto *anchor = getAnchor();
112+
auto *KPE = dyn_cast_or_null<KeyPathExpr>(anchor);
113+
if (!KPE)
114+
return false;
115+
116+
using ComponentKind = KeyPathExpr::Component::Kind;
117+
return llvm::any_of(getPath(), [&](const LocatorPathElt &elt) {
118+
if (!elt.isKeyPathComponent())
119+
return false;
120+
121+
auto index = elt.getValue();
122+
auto &component = KPE->getComponents()[index];
123+
return component.getKind() == ComponentKind::Subscript ||
124+
component.getKind() == ComponentKind::UnresolvedSubscript;
125+
});
126+
}
127+
104128
void ConstraintLocator::dump(SourceManager *sm) {
105129
dump(sm, llvm::errs());
106130
llvm::errs() << "\n";

lib/Sema/ConstraintLocator.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,10 @@ class ConstraintLocator : public llvm::FoldingSetNode {
486486
bool isKeyPathDynamicMember() const {
487487
return getKind() == PathElementKind::KeyPathDynamicMember;
488488
}
489+
490+
bool isKeyPathComponent() const {
491+
return getKind() == PathElementKind::KeyPathComponent;
492+
}
489493
};
490494

491495
/// Return the summary flags for an entire path.
@@ -518,6 +522,14 @@ class ConstraintLocator : public llvm::FoldingSetNode {
518522
/// e.g. `foo[0]` or `\Foo.[0]`
519523
bool isSubscriptMemberRef() const;
520524

525+
/// Determine whether given locator points to the choice picked as
526+
/// as result of the key path dynamic member lookup operation.
527+
bool isResultOfKeyPathDynamicMemberLookup() const;
528+
529+
/// Determine whether given locator points to a subscript component
530+
/// of the key path at some index.
531+
bool isKeyPathSubscriptComponent() const;
532+
521533
/// Produce a profile of this locator, for use in a folding set.
522534
static void Profile(llvm::FoldingSetNodeID &id, Expr *anchor,
523535
ArrayRef<PathElement> path);

0 commit comments

Comments
 (0)