Skip to content

Commit f26f136

Browse files
committed
[CS] Tweak dynamic member constraint generation
In order to fit with the new IUO model where functions with IUO results have the disjunction formed when matching result types, we need to update this logic to form applicable fn constraints for the implicit `x[dynamicMember:]` subscript call. This is done using a new ImplicitDynamicMemberSubscript locator path element to allow easy identification of what the right callee and argument list should be.
1 parent aba30e7 commit f26f136

File tree

3 files changed

+94
-42
lines changed

3 files changed

+94
-42
lines changed

include/swift/Sema/ConstraintLocatorPathElts.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,9 @@ CUSTOM_LOCATOR_PATH_ELT(PlaceholderType)
220220
/// The implicit conversion applied at a given location.
221221
CUSTOM_LOCATOR_PATH_ELT(ImplicitConversion)
222222

223+
/// An implicit call to a 'dynamicMember' subscript for a dynamic member lookup.
224+
SIMPLE_LOCATOR_PATH_ELT(ImplicitDynamicMemberSubscript)
225+
223226
/// The element of the closure body e.g. statement, declaration, or expression.
224227
CUSTOM_LOCATOR_PATH_ELT(ClosureBodyElement)
225228

lib/Sema/ConstraintLocator.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const {
9292
case ConstraintLocator::UnresolvedMemberChainResult:
9393
case ConstraintLocator::PlaceholderType:
9494
case ConstraintLocator::ImplicitConversion:
95+
case ConstraintLocator::ImplicitDynamicMemberSubscript:
9596
case ConstraintLocator::ClosureBodyElement:
9697
return 0;
9798

@@ -556,6 +557,10 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const {
556557
out << "closure body element";
557558
break;
558559

560+
case ConstraintLocator::ImplicitDynamicMemberSubscript:
561+
out << "implicit dynamic member subscript";
562+
break;
563+
559564
case ConstraintLocator::ImplicitConversion:
560565
auto convElt = elt.castTo<LocatorPathElt::ImplicitConversion>();
561566
out << "implicit conversion " << getName(convElt.getConversionKind());

lib/Sema/ConstraintSystem.cpp

Lines changed: 86 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -460,9 +460,21 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator(
460460
}
461461

462462
auto anchor = locator->getAnchor();
463+
auto path = locator->getPath();
464+
{
465+
// If we have an implicit x[dynamicMember:] subscript call, the callee
466+
// is given by the original member locator it is based on, which we can get
467+
// by stripping away the implicit member element and everything after it.
468+
auto iter = path.rbegin();
469+
using ImplicitSubscriptElt = LocatorPathElt::ImplicitDynamicMemberSubscript;
470+
if (locator->findLast<ImplicitSubscriptElt>(iter)) {
471+
auto newPath = path.drop_back(iter - path.rbegin() + 1);
472+
return getConstraintLocator(anchor, newPath);
473+
}
474+
}
475+
463476
assert(bool(anchor) && "Expected an anchor!");
464477

465-
auto path = locator->getPath();
466478
{
467479
// If we have a locator for a member found through key path dynamic member
468480
// lookup, then we need to chop off the elements after the
@@ -2664,6 +2676,7 @@ void ConstraintSystem::bindOverloadType(
26642676
ConstraintLocator *locator, DeclContext *useDC,
26652677
llvm::function_ref<void(unsigned int, Type, ConstraintLocator *)>
26662678
verifyThatArgumentIsHashable) {
2679+
auto &ctx = getASTContext();
26672680
auto choice = overload.choice;
26682681
auto openedType = overload.openedType;
26692682

@@ -2677,6 +2690,37 @@ void ConstraintSystem::bindOverloadType(
26772690
addConstraint(ConstraintKind::Bind, boundType, ty, locator);
26782691
}
26792692
};
2693+
auto addDynamicMemberSubscriptConstraints = [&](Type argTy, Type resultTy) {
2694+
// DynamicMemberLookup results are always a (dynamicMember: T1) -> T2
2695+
// subscript.
2696+
auto *fnTy = openedType->castTo<FunctionType>();
2697+
assert(fnTy->getParams().size() == 1 &&
2698+
"subscript always has one argument");
2699+
2700+
auto *callLoc = getConstraintLocator(
2701+
locator, LocatorPathElt::ImplicitDynamicMemberSubscript());
2702+
2703+
// Associate an argument list for the implicit x[dynamicMember:] subscript
2704+
// if we haven't already.
2705+
auto *&argList = ArgumentLists[getArgumentInfoLocator(callLoc)];
2706+
if (!argList) {
2707+
argList = ArgumentList::createImplicit(
2708+
ctx, {Argument(SourceLoc(), ctx.Id_dynamicMember, /*expr*/ nullptr)},
2709+
AllocationArena::ConstraintSolver);
2710+
}
2711+
2712+
auto *callerTy = FunctionType::get(
2713+
{FunctionType::Param(argTy, ctx.Id_dynamicMember)}, resultTy);
2714+
2715+
ConstraintLocatorBuilder builder(callLoc);
2716+
addConstraint(ConstraintKind::ApplicableFunction, callerTy, fnTy,
2717+
builder.withPathElement(ConstraintLocator::ApplyFunction));
2718+
2719+
if (isExpr<KeyPathExpr>(locator->getAnchor())) {
2720+
auto paramTy = fnTy->getParams()[0].getParameterType();
2721+
verifyThatArgumentIsHashable(/*idx*/ 0, paramTy, locator);
2722+
}
2723+
};
26802724
switch (choice.getKind()) {
26812725
case OverloadChoiceKind::Decl:
26822726
case OverloadChoiceKind::DeclViaBridge:
@@ -2696,25 +2740,20 @@ void ConstraintSystem::bindOverloadType(
26962740
// makes the index default to String if otherwise unconstrained.
26972741
assert(refFnType->getParams().size() == 1 &&
26982742
"subscript always has one arg");
2699-
auto argType = refFnType->getParams()[0].getPlainType();
27002743

27012744
auto stringLiteral =
27022745
TypeChecker::getProtocol(getASTContext(), choice.getDecl()->getLoc(),
27032746
KnownProtocolKind::ExpressibleByStringLiteral);
27042747
if (!stringLiteral)
27052748
return;
27062749

2707-
addConstraint(ConstraintKind::LiteralConformsTo, argType,
2750+
// Form constraints for a x[dynamicMember:] subscript with a string literal
2751+
// argument, where the overload type is bound to the result to model the
2752+
// fact that this a property access in the source.
2753+
auto argTy = createTypeVariable(locator, /*options*/ 0);
2754+
addConstraint(ConstraintKind::LiteralConformsTo, argTy,
27082755
stringLiteral->getDeclaredInterfaceType(), locator);
2709-
2710-
// If this is used inside of the keypath expression, we need to make
2711-
// sure that argument is Hashable.
2712-
if (isExpr<KeyPathExpr>(locator->getAnchor()))
2713-
verifyThatArgumentIsHashable(0, argType, locator);
2714-
2715-
// The resolved decl is for subscript(dynamicMember:), however the original
2716-
// member constraint was for a property. Therefore we need to bind to the
2717-
// result type.
2756+
addDynamicMemberSubscriptConstraints(argTy, refFnType->getResult());
27182757
bindTypeOrIUO(refFnType->getResult());
27192758
return;
27202759
}
@@ -2764,9 +2803,9 @@ void ConstraintSystem::bindOverloadType(
27642803
// preferred over key path dynamic member lookup.
27652804
increaseScore(SK_KeyPathSubscript);
27662805

2767-
auto dynamicResultTy = boundType->castTo<TypeVariableType>();
2806+
auto boundTypeVar = boundType->castTo<TypeVariableType>();
27682807
auto constraints = getConstraintGraph().gatherConstraints(
2769-
dynamicResultTy, ConstraintGraph::GatheringKind::EquivalenceClass,
2808+
boundTypeVar, ConstraintGraph::GatheringKind::EquivalenceClass,
27702809
[](Constraint *constraint) {
27712810
return constraint->getKind() == ConstraintKind::ApplicableFunction;
27722811
});
@@ -2790,20 +2829,11 @@ void ConstraintSystem::bindOverloadType(
27902829
// - Original result type `$T_R` is going to represent result of
27912830
// the `[dynamicMember: \.[0]]` invocation.
27922831

2793-
// Result of the `WritableKeyPath` is going to be l-value type,
2794-
// let's adjust l-valueness of the result type to accommodate that.
2795-
//
2796-
// This is required because we are binding result of the subscript
2797-
// to its "member type" which becomes dynamic result type. We could
2798-
// form additional `applicable fn` constraint here and bind it to a
2799-
// function type, but it would create inconsistency with how properties
2800-
// are handled, which means more special handling in CSApply.
2801-
if (keyPathTy->isWritableKeyPath() ||
2802-
keyPathTy->isReferenceWritableKeyPath())
2803-
dynamicResultTy->getImpl().setCanBindToLValue(getSavedBindings(),
2804-
/*enabled=*/true);
2805-
2806-
auto fnType = applicableFn->getFirstType()->castTo<FunctionType>();
2832+
// The function type of the original call-site. We'll want to create a
2833+
// new applicable fn constraint using its parameter along with a fresh
2834+
// type variable for the result of the inner subscript.
2835+
auto originalCallerTy =
2836+
applicableFn->getFirstType()->castTo<FunctionType>();
28072837

28082838
auto subscriptResultTy = createTypeVariable(
28092839
getConstraintLocator(locator->getAnchor(),
@@ -2812,35 +2842,41 @@ void ConstraintSystem::bindOverloadType(
28122842

28132843
// FIXME: Verify ExtInfo state is correct, not working by accident.
28142844
FunctionType::ExtInfo info;
2815-
auto adjustedFnTy =
2816-
FunctionType::get(fnType->getParams(), subscriptResultTy, info);
2845+
auto adjustedFnTy = FunctionType::get(originalCallerTy->getParams(),
2846+
subscriptResultTy, info);
28172847

2848+
// Add a constraint for the inner application that uses the args of the
2849+
// original call-site, and a fresh type var result equal to the leaf type.
28182850
ConstraintLocatorBuilder kpLocBuilder(keyPathLoc);
28192851
addConstraint(
28202852
ConstraintKind::ApplicableFunction, adjustedFnTy, memberTy,
28212853
kpLocBuilder.withPathElement(ConstraintLocator::ApplyFunction));
28222854

2823-
addConstraint(ConstraintKind::Bind, dynamicResultTy, fnType->getResult(),
2824-
keyPathLoc);
2855+
addConstraint(ConstraintKind::FunctionResult, boundType,
2856+
originalCallerTy->getResult(), keyPathLoc);
28252857

28262858
addConstraint(ConstraintKind::Equal, subscriptResultTy, leafTy,
28272859
keyPathLoc);
2860+
2861+
addDynamicMemberSubscriptConstraints(/*argTy*/ keyPathTy,
2862+
fnType->getResult());
2863+
2864+
// Bind the overload type to the opened type as usual to match the fact
2865+
// that this is a subscript in the source.
2866+
bindTypeOrIUO(fnType);
28282867
} else {
28292868
// Since member type is going to be bound to "leaf" generic parameter
28302869
// of the keypath, it has to be an r-value always, so let's add a new
28312870
// constraint to represent that conversion instead of loading member
28322871
// type into "leaf" directly.
28332872
addConstraint(ConstraintKind::Equal, memberTy, leafTy, keyPathLoc);
2834-
}
2873+
addDynamicMemberSubscriptConstraints(/*argTy*/ keyPathTy,
2874+
fnType->getResult());
28352875

2836-
if (isExpr<KeyPathExpr>(locator->getAnchor()))
2837-
verifyThatArgumentIsHashable(0, keyPathTy, locator);
2838-
2839-
// The resolved decl is for subscript(dynamicMember:), however the
2840-
// original member constraint was either for a property, or we've
2841-
// re-purposed the overload type variable to represent the result type of
2842-
// the subscript. In both cases, we need to bind to the result type.
2843-
bindTypeOrIUO(fnType->getResult());
2876+
// Bind the overload type to the result to model the fact that this a
2877+
// property access in the source.
2878+
bindTypeOrIUO(fnType->getResult());
2879+
}
28442880
return;
28452881
}
28462882
}
@@ -4534,7 +4570,8 @@ void constraints::simplifyLocator(ASTNode &anchor,
45344570
continue;
45354571
}
45364572

4537-
case ConstraintLocator::KeyPathDynamicMember: {
4573+
case ConstraintLocator::KeyPathDynamicMember:
4574+
case ConstraintLocator::ImplicitDynamicMemberSubscript: {
45384575
// Key path dynamic member lookup should be completely transparent.
45394576
path = path.slice(1);
45404577
continue;
@@ -4716,6 +4753,13 @@ ConstraintSystem::getArgumentInfoLocator(ConstraintLocator *locator) {
47164753
if (auto *UME = getAsExpr<UnresolvedMemberExpr>(anchor))
47174754
return getConstraintLocator(UME);
47184755

4756+
// All implicit x[dynamicMember:] subscript calls can share the same argument
4757+
// list.
4758+
if (locator->findLast<LocatorPathElt::ImplicitDynamicMemberSubscript>()) {
4759+
return getConstraintLocator(
4760+
ASTNode(), LocatorPathElt::ImplicitDynamicMemberSubscript());
4761+
}
4762+
47194763
auto path = locator->getPath();
47204764
{
47214765
// If this is for a dynamic member reference, the argument info is for the

0 commit comments

Comments
 (0)