Skip to content

Commit cde23ec

Browse files
committed
[ConstraintSystem] Implement keypath dynamic member lookup
Implement keypath based dynamic member lookup which augments functionality of existing @dynamicMemberLookup attribute. Two new subscript overloads are accepted: ``` subscript(dynamicMember member: KeyPath<T, ...>) -> ... subscript(dynamicmember member: WritableKeyPath<T, ...>) -> ... ``` Example: ```swift struct Point { let x: Int var y: Int } @dynamicMemberLookup struct Lens<T> { var obj: T init(_ obj: T) { self.obj = obj } subscript<U>(dynamicMember member: KeyPath<T, U>) -> Lens<U> { get { return Lens<U>(obj[keyPath: member]) } } subscript<U>(dynamicMember member: WritableKeyPath<T, U>) -> Lens<U> { get { return Lens<U>(obj[keyPath: member]) } set { obj[keyPath: member] = newValue.obj } } } var lens = Lens(Point(x: 0, y: 0)) _ = lens.x // converted into `lens[dynamicMember: KeyPath<Point, Int>` _ = lens.y = Lens(10) // converted into `lens[dynamicMember: WritableKeyPath<Point, Int>]` ```
1 parent f95d9b0 commit cde23ec

File tree

3 files changed

+114
-2
lines changed

3 files changed

+114
-2
lines changed

lib/Sema/CSSolver.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ Solution ConstraintSystem::finalize() {
172172
for (auto &e : CheckedConformances)
173173
solution.Conformances.push_back({e.first, e.second});
174174

175+
for (auto &arg : DynamicMemberArguments)
176+
solution.DynamicMemberArguments.insert(arg);
177+
175178
return solution;
176179
}
177180

@@ -242,6 +245,11 @@ void ConstraintSystem::applySolution(const Solution &solution) {
242245
// Register any missing members encountered along this path.
243246
MissingMembers.insert(solution.MissingMembers.begin(),
244247
solution.MissingMembers.end());
248+
249+
// Register any implicitly generated argument used by keypath
250+
// based dynamic member lookup.
251+
DynamicMemberArguments.append(solution.DynamicMemberArguments.begin(),
252+
solution.DynamicMemberArguments.end());
245253
}
246254

247255
/// Restore the type variable bindings to what they were before
@@ -425,6 +433,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs)
425433
numMissingMembers = cs.MissingMembers.size();
426434
numDisabledConstraints = cs.solverState->getNumDisabledConstraints();
427435
numFavoredConstraints = cs.solverState->getNumFavoredConstraints();
436+
numDynamicMemberArguments = cs.DynamicMemberArguments.size();
428437

429438
PreviousScore = cs.CurrentScore;
430439

@@ -479,6 +488,10 @@ ConstraintSystem::SolverScope::~SolverScope() {
479488
// Remove any missing members found along the current path.
480489
truncate(cs.MissingMembers, numMissingMembers);
481490

491+
// Remove any implicitly generated arguments used by keypath
492+
// based dynamic lookup generated along the current path.
493+
truncate(cs.DynamicMemberArguments, numDynamicMemberArguments);
494+
482495
// Reset the previous score.
483496
cs.CurrentScore = PreviousScore;
484497

lib/Sema/ConstraintSystem.cpp

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1883,7 +1883,52 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
18831883
}
18841884

18851885
if (kind == OverloadChoiceKind::KeyPathDynamicMemberLookup) {
1886-
assert(false && "not yet implemented");
1886+
auto *fnType = refType->castTo<FunctionType>();
1887+
assert(fnType->getParams().size() == 1 &&
1888+
"subscript always has one argument");
1889+
// Parameter type is KeyPath<T, U> where `T` is a root type
1890+
// and U is a leaf type (aka member type).
1891+
auto keyPathTy =
1892+
fnType->getParams()[0].getPlainType()->castTo<BoundGenericType>();
1893+
1894+
refType = fnType->getResult();
1895+
1896+
auto *keyPathExpr = buildImplicitDynamicKeyPathArgument(
1897+
locator->getAnchor(), keyPathTy, locator);
1898+
1899+
// If argument couldn't be built, let's fail this choice.
1900+
if (!keyPathExpr) {
1901+
failedConstraint = Constraint::create(*this, ConstraintKind::Bind,
1902+
boundType, refType, locator);
1903+
return;
1904+
}
1905+
1906+
auto *keyPathLocator = getConstraintLocator(keyPathExpr);
1907+
auto *componentLoc = getConstraintLocator(
1908+
keyPathExpr, LocatorPathElt::getKeyPathComponent(0));
1909+
1910+
auto rootTy = keyPathTy->getGenericArgs()[0];
1911+
// Member would either point to mutable or immutable property, we
1912+
// don't which at the moment, so let's allow its type to be l-value.
1913+
auto memberTy = createTypeVariable(componentLoc, TVO_CanBindToLValue);
1914+
// Attempt to lookup a member with a give name in the root type and
1915+
// assign result to the leaf type of the keypath.
1916+
addValueMemberConstraint(LValueType::get(rootTy), choice.getName(),
1917+
memberTy, useDC, FunctionRefKind::Unapplied,
1918+
/*outerAlternatives=*/{}, componentLoc);
1919+
1920+
auto rvalueMemberTy = createTypeVariable(keyPathLocator);
1921+
// Since member type is going to be bound to "leaf" generic parameter
1922+
// of the keypath, it has to be an r-value always, so let's add a new
1923+
// constraint to preserve that.
1924+
addConstraint(ConstraintKind::Equal, memberTy, rvalueMemberTy,
1925+
keyPathLocator);
1926+
1927+
// For a new keypath constraint which is going to check whether given
1928+
// overload is correct.
1929+
addUnsolvedConstraint(Constraint::create(*this, ConstraintKind::KeyPath,
1930+
keyPathTy, rootTy,
1931+
rvalueMemberTy, keyPathLocator));
18871932
}
18881933
break;
18891934
}
@@ -2556,3 +2601,35 @@ void ConstraintSystem::generateConstraints(
25562601
recordChoice(constraints, index, choices[index]);
25572602
}
25582603
}
2604+
2605+
KeyPathExpr *ConstraintSystem::buildImplicitDynamicKeyPathArgument(
2606+
Expr *member, BoundGenericType *keyPathTy, ConstraintLocator *locator) {
2607+
// Only properties are support at the moment.
2608+
auto *UDE = dyn_cast<UnresolvedDotExpr>(member);
2609+
if (!UDE)
2610+
return nullptr;
2611+
2612+
auto &ctx = getASTContext();
2613+
auto dotLoc = UDE->getDotLoc();
2614+
2615+
auto *property = new (ctx)
2616+
UnresolvedDotExpr(new (ctx) KeyPathDotExpr(dotLoc), dotLoc,
2617+
UDE->getName(), UDE->getNameLoc(), /*isImplicit=*/true);
2618+
2619+
// Now, let's create a KeyPath expression itself.
2620+
auto *keyPath = new (ctx) KeyPathExpr(/*backslashLoc=*/dotLoc,
2621+
/*parsedRoot=*/nullptr,
2622+
/*parsedPath=*/property,
2623+
/*isImplicit=*/true);
2624+
2625+
// Let's form a single unrsolved property component this keypath is
2626+
// going to refer to.
2627+
keyPath->resolveComponents(
2628+
ctx, {KeyPathExpr::Component::forUnresolvedProperty(
2629+
property->getName(), property->getStartLoc())});
2630+
setType(keyPath, keyPathTy);
2631+
2632+
// Register newly generated argument in the constraint system.
2633+
DynamicMemberArguments.push_back({locator, keyPath});
2634+
return keyPath;
2635+
}

lib/Sema/ConstraintSystem.h

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,11 @@ class Solution {
613613
llvm::SmallVector<std::pair<ConstraintLocator *, ProtocolConformanceRef>, 8>
614614
Conformances;
615615

616+
/// The set of implicitly generated arguments used by keypath based
617+
/// dynamic lookup.
618+
llvm::SmallDenseMap<ConstraintLocator *, KeyPathExpr *>
619+
DynamicMemberArguments;
620+
616621
/// Simplify the given type by substituting all occurrences of
617622
/// type variables for their fixed types.
618623
Type simplifyType(Type type) const;
@@ -1076,6 +1081,11 @@ class ConstraintSystem {
10761081
SmallVector<std::pair<ConstraintLocator *, ProtocolConformanceRef>, 8>
10771082
CheckedConformances;
10781083

1084+
/// A cache that stores implicitly generated arguments for keypath based
1085+
/// dynamic lookup.
1086+
SmallVector<std::pair<ConstraintLocator *, KeyPathExpr *>, 4>
1087+
DynamicMemberArguments;
1088+
10791089
public:
10801090
/// The locators of \c Defaultable constraints whose defaults were used.
10811091
SmallVector<ConstraintLocator *, 8> DefaultedConstraints;
@@ -1543,6 +1553,8 @@ class ConstraintSystem {
15431553

15441554
unsigned numFavoredConstraints;
15451555

1556+
unsigned numDynamicMemberArguments;
1557+
15461558
/// The previous score.
15471559
Score PreviousScore;
15481560

@@ -2748,7 +2760,17 @@ class ConstraintSystem {
27482760
ConstraintLocator *memberLocator,
27492761
bool includeInaccessibleMembers);
27502762

2751-
private:
2763+
private:
2764+
/// Build an implicit argument for keypath based dynamic lookup,
2765+
/// which consists of KeyPath expression and a single component.
2766+
///
2767+
/// \param member The member being dynamically lookuped up.
2768+
/// \param keyPathTy The type of the keypath argument.
2769+
/// \param locator The locator to be associated with new argument.
2770+
KeyPathExpr *buildImplicitDynamicKeyPathArgument(Expr *member,
2771+
BoundGenericType *keyPathTy,
2772+
ConstraintLocator *locator);
2773+
27522774
/// Attempt to simplify the given construction constraint.
27532775
///
27542776
/// \param valueType The type being constructed.

0 commit comments

Comments
 (0)