Skip to content

Commit f5ea921

Browse files
authored
Merge pull request #23812 from xedin/keypath-dynamic-lookup-5.1
[5.1][TypeChecker] SE-0252: KeyPath Dynamic Member Lookup
2 parents b84ed10 + 0d4ddb0 commit f5ea921

21 files changed

+1462
-260
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,8 +1057,8 @@ ERROR(missing_dynamic_callable_kwargs_method,none,
10571057

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

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

lib/Sema/CSApply.cpp

Lines changed: 389 additions & 176 deletions
Large diffs are not rendered by default.

lib/Sema/CSDiag.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,8 @@ void FailureDiagnosis::diagnoseUnviableLookupResults(
811811
if (sameProblem) {
812812
switch (firstProblem) {
813813
case MemberLookupResult::UR_LabelMismatch:
814+
case MemberLookupResult::UR_WritableKeyPathOnReadOnlyMember:
815+
case MemberLookupResult::UR_ReferenceWritableKeyPathOnMutatingMember:
814816
break;
815817
case MemberLookupResult::UR_UnavailableInExistential:
816818
diagnose(loc, diag::could_not_use_member_on_existential,

lib/Sema/CSDiagnostics.cpp

Lines changed: 50 additions & 4 deletions
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

Lines changed: 4 additions & 0 deletions
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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3259,6 +3259,38 @@ namespace {
32593259
EPE->setSemanticExpr(nullptr);
32603260
}
32613261

3262+
// If this expression represents keypath based dynamic member
3263+
// lookup, let's convert it back to the original form of
3264+
// member or subscript reference.
3265+
if (auto *SE = dyn_cast<SubscriptExpr>(expr)) {
3266+
if (auto *TE = dyn_cast<TupleExpr>(SE->getIndex())) {
3267+
auto isImplicitKeyPathExpr = [](Expr *argExpr) -> bool {
3268+
if (auto *KP = dyn_cast<KeyPathExpr>(argExpr))
3269+
return KP->isImplicit();
3270+
return false;
3271+
};
3272+
3273+
if (TE->isImplicit() && TE->getNumElements() == 1 &&
3274+
TE->getElementName(0) == TC.Context.Id_dynamicMember &&
3275+
isImplicitKeyPathExpr(TE->getElement(0))) {
3276+
auto *keyPathExpr = cast<KeyPathExpr>(TE->getElement(0));
3277+
auto *componentExpr = keyPathExpr->getParsedPath();
3278+
3279+
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(componentExpr)) {
3280+
UDE->setBase(SE->getBase());
3281+
return {true, UDE};
3282+
}
3283+
3284+
if (auto *subscript = dyn_cast<SubscriptExpr>(componentExpr)) {
3285+
subscript->setBase(SE->getBase());
3286+
return {true, subscript};
3287+
}
3288+
3289+
llvm_unreachable("unknown keypath component type");
3290+
}
3291+
}
3292+
}
3293+
32623294
// Now, we're ready to walk into sub expressions.
32633295
return {true, expr};
32643296
}

lib/Sema/CSRanking.cpp

Lines changed: 16 additions & 0 deletions
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)