Skip to content

Commit 16b6501

Browse files
authored
Merge pull request #23436 from xedin/keypath-dynamic-lookup
[SE-0252][TypeChecker] Keypath Dynamic Member Lookup
2 parents 36b562d + b388d83 commit 16b6501

21 files changed

+1454
-260
lines changed

include/swift/AST/DiagnosticsSema.def

+2-2
Original file line numberDiff line numberDiff line change
@@ -1053,8 +1053,8 @@ ERROR(missing_dynamic_callable_kwargs_method,none,
10531053

10541054
ERROR(invalid_dynamic_member_lookup_type,none,
10551055
"@dynamicMemberLookup attribute requires %0 to have a "
1056-
"'subscript(dynamicMember:)' method with an "
1057-
"'ExpressibleByStringLiteral' parameter", (Type))
1056+
"'subscript(dynamicMember:)' method that accepts either "
1057+
"'ExpressibleByStringLiteral' or a keypath", (Type))
10581058

10591059
ERROR(string_index_not_integer,none,
10601060
"String must not be indexed with %0, it has variable size elements",

lib/Sema/CSApply.cpp

+381-176
Large diffs are not rendered by default.

lib/Sema/CSDiag.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,8 @@ void FailureDiagnosis::diagnoseUnviableLookupResults(
756756
if (sameProblem) {
757757
switch (firstProblem) {
758758
case MemberLookupResult::UR_LabelMismatch:
759+
case MemberLookupResult::UR_WritableKeyPathOnReadOnlyMember:
760+
case MemberLookupResult::UR_ReferenceWritableKeyPathOnMutatingMember:
759761
break;
760762
case MemberLookupResult::UR_UnavailableInExistential:
761763
diagnose(loc, diag::could_not_use_member_on_existential,

lib/Sema/CSDiagnostics.cpp

+50-4
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,7 @@ bool MissingOptionalUnwrapFailure::diagnoseAsError() {
776776
bool RValueTreatedAsLValueFailure::diagnoseAsError() {
777777
Diag<StringRef> subElementDiagID;
778778
Diag<Type> rvalueDiagID = diag::assignment_lhs_not_lvalue;
779-
Expr *diagExpr = getLocator()->getAnchor();
779+
Expr *diagExpr = getRawAnchor();
780780
SourceLoc loc = diagExpr->getLoc();
781781

782782
if (auto assignExpr = dyn_cast<AssignExpr>(diagExpr)) {
@@ -854,10 +854,18 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() {
854854
}
855855
}
856856

857-
if (auto resolvedOverload = getResolvedOverload(getLocator()))
857+
if (auto resolvedOverload = getResolvedOverload(getLocator())) {
858858
if (resolvedOverload->Choice.getKind() ==
859859
OverloadChoiceKind::DynamicMemberLookup)
860860
subElementDiagID = diag::assignment_dynamic_property_has_immutable_base;
861+
862+
if (resolvedOverload->Choice.getKind() ==
863+
OverloadChoiceKind::KeyPathDynamicMemberLookup) {
864+
if (!getType(member->getBase())->hasLValueType())
865+
subElementDiagID =
866+
diag::assignment_dynamic_property_has_immutable_base;
867+
}
868+
}
861869
} else if (auto sub = dyn_cast<SubscriptExpr>(diagExpr)) {
862870
subElementDiagID = diag::assignment_subscript_has_immutable_base;
863871
} else {
@@ -1202,7 +1210,7 @@ AssignmentFailure::resolveImmutableBase(Expr *expr) const {
12021210
if (!member) {
12031211
auto loc =
12041212
cs.getConstraintLocator(SE, ConstraintLocator::SubscriptMember);
1205-
member = dyn_cast_or_null<SubscriptDecl>(cs.findResolvedMemberRef(loc));
1213+
member = dyn_cast_or_null<SubscriptDecl>(getMemberRef(loc));
12061214
}
12071215

12081216
// If it isn't settable, return it.
@@ -1231,9 +1239,10 @@ AssignmentFailure::resolveImmutableBase(Expr *expr) const {
12311239
// If we found a decl for the UDE, check it.
12321240
auto loc = cs.getConstraintLocator(UDE, ConstraintLocator::Member);
12331241

1242+
auto *member = getMemberRef(loc);
12341243
// If we can resolve a member, we can determine whether it is settable in
12351244
// this context.
1236-
if (auto *member = cs.findResolvedMemberRef(loc)) {
1245+
if (member) {
12371246
auto *memberVD = dyn_cast<VarDecl>(member);
12381247

12391248
// If the member isn't a vardecl (e.g. its a funcdecl), or it isn't
@@ -1281,6 +1290,43 @@ AssignmentFailure::resolveImmutableBase(Expr *expr) const {
12811290
return {expr, nullptr};
12821291
}
12831292

1293+
ValueDecl *AssignmentFailure::getMemberRef(ConstraintLocator *locator) const {
1294+
auto member = getOverloadChoiceIfAvailable(locator);
1295+
if (!member || !member->choice.isDecl())
1296+
return nullptr;
1297+
1298+
auto *DC = getDC();
1299+
auto &TC = getTypeChecker();
1300+
1301+
auto *decl = member->choice.getDecl();
1302+
if (isa<SubscriptDecl>(decl) &&
1303+
isValidDynamicMemberLookupSubscript(cast<SubscriptDecl>(decl), DC, TC)) {
1304+
auto *subscript = cast<SubscriptDecl>(decl);
1305+
// If this is a keypath dynamic member lookup, we have to
1306+
// adjust the locator to find member referred by it.
1307+
if (isValidKeyPathDynamicMemberLookup(subscript, TC)) {
1308+
auto &cs = getConstraintSystem();
1309+
// Type has a following format:
1310+
// `(Self) -> (dynamicMember: {Writable}KeyPath<T, U>) -> U`
1311+
auto *fullType = member->openedFullType->castTo<FunctionType>();
1312+
auto *fnType = fullType->getResult()->castTo<FunctionType>();
1313+
1314+
auto paramTy = fnType->getParams()[0].getPlainType();
1315+
auto keyPath = paramTy->getAnyNominal();
1316+
auto memberLoc = cs.getConstraintLocator(
1317+
locator, LocatorPathElt::getKeyPathDynamicMember(keyPath));
1318+
1319+
auto memberRef = getOverloadChoiceIfAvailable(memberLoc);
1320+
return memberRef ? memberRef->choice.getDecl() : nullptr;
1321+
}
1322+
1323+
// If this is a string based dynamic lookup, there is no member declaration.
1324+
return nullptr;
1325+
}
1326+
1327+
return decl;
1328+
}
1329+
12841330
Diag<StringRef> AssignmentFailure::findDeclDiagonstic(ASTContext &ctx,
12851331
Expr *destExpr) {
12861332
if (isa<ApplyExpr>(destExpr) || isa<SelfApplyExpr>(destExpr))

lib/Sema/CSDiagnostics.h

+4
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,10 @@ class AssignmentFailure final : public FailureDiagnostic {
647647
isLoadedLValue(ifExpr->getElseExpr());
648648
return false;
649649
}
650+
651+
/// Retrive an member reference associated with given member
652+
/// looking through dynamic member lookup on the way.
653+
ValueDecl *getMemberRef(ConstraintLocator *locator) const;
650654
};
651655

652656
/// Intended to diagnose any possible contextual failure

lib/Sema/CSGen.cpp

+32
Original file line numberDiff line numberDiff line change
@@ -3274,6 +3274,38 @@ namespace {
32743274
expr = sanitizeArgumentList(expr);
32753275
}
32763276

3277+
// If this expression represents keypath based dynamic member
3278+
// lookup, let's convert it back to the original form of
3279+
// member or subscript reference.
3280+
if (auto *SE = dyn_cast<SubscriptExpr>(expr)) {
3281+
if (auto *TE = dyn_cast<TupleExpr>(SE->getIndex())) {
3282+
auto isImplicitKeyPathExpr = [](Expr *argExpr) -> bool {
3283+
if (auto *KP = dyn_cast<KeyPathExpr>(argExpr))
3284+
return KP->isImplicit();
3285+
return false;
3286+
};
3287+
3288+
if (TE->isImplicit() && TE->getNumElements() == 1 &&
3289+
TE->getElementName(0) == TC.Context.Id_dynamicMember &&
3290+
isImplicitKeyPathExpr(TE->getElement(0))) {
3291+
auto *keyPathExpr = cast<KeyPathExpr>(TE->getElement(0));
3292+
auto *componentExpr = keyPathExpr->getParsedPath();
3293+
3294+
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(componentExpr)) {
3295+
UDE->setBase(SE->getBase());
3296+
return {true, UDE};
3297+
}
3298+
3299+
if (auto *subscript = dyn_cast<SubscriptExpr>(componentExpr)) {
3300+
subscript->setBase(SE->getBase());
3301+
return {true, subscript};
3302+
}
3303+
3304+
llvm_unreachable("unknown keypath component type");
3305+
}
3306+
}
3307+
}
3308+
32773309
// Now, we're ready to walk into sub expressions.
32783310
return {true, expr};
32793311
}

lib/Sema/CSRanking.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ static bool sameOverloadChoice(const OverloadChoice &x,
151151
case OverloadChoiceKind::DeclViaBridge:
152152
case OverloadChoiceKind::DeclViaUnwrappedOptional:
153153
case OverloadChoiceKind::DynamicMemberLookup:
154+
case OverloadChoiceKind::KeyPathDynamicMemberLookup:
154155
return sameDecl(x.getDecl(), y.getDecl());
155156

156157
case OverloadChoiceKind::TupleIndex:
@@ -875,6 +876,20 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
875876
continue;
876877
}
877878

879+
// Dynamic member lookup through a keypath is better than one using string
880+
// because it carries more type information.
881+
if (choice1.getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup &&
882+
choice2.getKind() == OverloadChoiceKind::DynamicMemberLookup) {
883+
score1 += weight;
884+
continue;
885+
}
886+
887+
if (choice1.getKind() == OverloadChoiceKind::DynamicMemberLookup &&
888+
choice2.getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup) {
889+
score2 += weight;
890+
continue;
891+
}
892+
878893
continue;
879894
}
880895

@@ -893,6 +908,7 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
893908
case OverloadChoiceKind::DeclViaBridge:
894909
case OverloadChoiceKind::DeclViaUnwrappedOptional:
895910
case OverloadChoiceKind::DynamicMemberLookup:
911+
case OverloadChoiceKind::KeyPathDynamicMemberLookup:
896912
break;
897913
}
898914

0 commit comments

Comments
 (0)