Skip to content

Commit 7092053

Browse files
authored
Merge pull request #84479 from tshortli/expr-availability-key-path-mutability-in-conversions
Sema: Infer key path immutability during availability checking
2 parents d579027 + ee988c0 commit 7092053

File tree

12 files changed

+110
-51
lines changed

12 files changed

+110
-51
lines changed

include/swift/AST/Types.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,12 @@ class alignas(1 << TypeAlignInBits) TypeBase
11031103
/// Check if this is a std.string type from C++.
11041104
bool isCxxString();
11051105

1106+
/// Check if this type is known to represent key paths.
1107+
bool isKnownKeyPathType();
1108+
1109+
/// Check if this type is known to represent immutable key paths.
1110+
bool isKnownImmutableKeyPathType();
1111+
11061112
/// Check if this is either an Array, Set or Dictionary collection type defined
11071113
/// at the top level of the Swift module
11081114
bool isKnownStdlibCollectionType();

include/swift/Sema/ConstraintSystem.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6525,10 +6525,6 @@ class TypeVarRefCollector : public ASTWalker {
65256525
}
65266526
};
65276527

6528-
/// Determine whether given type is a known one
6529-
/// for a key path `{Any, Partial, Writable, ReferenceWritable}KeyPath`.
6530-
bool isKnownKeyPathType(Type type);
6531-
65326528
/// Determine whether the given type is a PartialKeyPath and
65336529
/// AnyKeyPath or existential type thererof, for example,
65346530
/// `PartialKeyPath<...> & Sendable`.

lib/AST/Type.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,15 @@ bool TypeBase::isCxxString() {
13091309
ctx.Id_basic_string.is(clangDecl->getName());
13101310
}
13111311

1312+
bool TypeBase::isKnownKeyPathType() {
1313+
return isKeyPath() || isWritableKeyPath() || isReferenceWritableKeyPath() ||
1314+
isPartialKeyPath() || isAnyKeyPath();
1315+
}
1316+
1317+
bool TypeBase::isKnownImmutableKeyPathType() {
1318+
return isKeyPath() || isPartialKeyPath() || isAnyKeyPath();
1319+
}
1320+
13121321
bool TypeBase::isKnownStdlibCollectionType() {
13131322
if (isArray() || isDictionary() || isSet()) {
13141323
return true;

lib/IDE/SelectedOverloadInfo.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ swift::ide::getSelectedOverloadInfo(const Solution &S,
7979
fnType->getParams()[0].getPlainType()->castTo<BoundGenericType>();
8080

8181
auto *keyPathDecl = keyPathTy->getAnyNominal();
82-
assert(isKnownKeyPathType(keyPathTy) &&
82+
assert(keyPathTy->isKnownKeyPathType() &&
8383
"parameter is supposed to be a keypath");
8484

8585
auto KeyPathDynamicLocator = CS.getConstraintLocator(

lib/Sema/CSApply.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2689,7 +2689,7 @@ namespace {
26892689
Type keyPathTy = paramType;
26902690
if (auto *existential = keyPathTy->getAs<ExistentialType>()) {
26912691
keyPathTy = existential->getSuperclass();
2692-
assert(isKnownKeyPathType(keyPathTy));
2692+
assert(keyPathTy->isKnownKeyPathType());
26932693
}
26942694

26952695
SmallVector<Component, 2> components;

lib/Sema/CSBindings.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ void BindingSet::inferTransitiveBindings() {
513513
auto bindingTy = binding.BindingType->lookThroughAllOptionalTypes();
514514

515515
Type inferredRootTy;
516-
if (isKnownKeyPathType(bindingTy)) {
516+
if (bindingTy->isKnownKeyPathType()) {
517517
// AnyKeyPath doesn't have a root type.
518518
if (bindingTy->isAnyKeyPath())
519519
continue;
@@ -728,7 +728,8 @@ bool BindingSet::finalize(bool transitive) {
728728
for (const auto &binding : Bindings) {
729729
auto bindingTy = binding.BindingType->lookThroughAllOptionalTypes();
730730

731-
assert(isKnownKeyPathType(bindingTy) || bindingTy->is<FunctionType>());
731+
assert(bindingTy->isKnownKeyPathType() ||
732+
bindingTy->is<FunctionType>());
732733

733734
// Functions don't have capability so we can simply add them.
734735
if (auto *fnType = bindingTy->getAs<FunctionType>()) {
@@ -1706,15 +1707,15 @@ PotentialBindings::inferFromRelational(ConstraintSystem &CS,
17061707
if (type->isExistentialType()) {
17071708
auto layout = type->getExistentialLayout();
17081709
if (auto superclass = layout.explicitSuperclass) {
1709-
if (isKnownKeyPathType(superclass)) {
1710+
if (superclass->isKnownKeyPathType()) {
17101711
type = superclass;
17111712
objectTy = superclass;
17121713
}
17131714
}
17141715
}
17151716
}
17161717

1717-
if (!(isKnownKeyPathType(objectTy) || objectTy->is<AnyFunctionType>()))
1718+
if (!(objectTy->isKnownKeyPathType() || objectTy->is<AnyFunctionType>()))
17181719
return std::nullopt;
17191720
}
17201721

lib/Sema/CSDiagnostics.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2262,7 +2262,7 @@ bool AssignmentFailure::diagnoseAsError() {
22622262

22632263
auto type = getType(immutableExpr);
22642264

2265-
if (isKnownKeyPathType(type))
2265+
if (type->isKnownKeyPathType())
22662266
message += " is read-only";
22672267
else if (VD->isCaptureList())
22682268
message += " is an immutable capture";
@@ -7487,7 +7487,7 @@ bool ArgumentMismatchFailure::diagnoseAsError() {
74877487

74887488
// Unresolved key path argument requires a tailored diagnostic
74897489
// that doesn't mention a fallback type - `KeyPath<_, _>`.
7490-
if (argType->isKeyPath() && !isKnownKeyPathType(paramType)) {
7490+
if (argType->isKeyPath() && !paramType->isKnownKeyPathType()) {
74917491
auto keyPathTy = argType->castTo<BoundGenericType>();
74927492
auto rootTy = keyPathTy->getGenericArgs()[0];
74937493
if (rootTy->is<UnresolvedType>()) {
@@ -7852,7 +7852,7 @@ bool ArgumentMismatchFailure::diagnoseKeyPathAsFunctionResultMismatch() const {
78527852
auto argType = getFromType();
78537853
auto paramType = getToType();
78547854

7855-
if (!isKnownKeyPathType(argType))
7855+
if (!argType->isKnownKeyPathType())
78567856
return false;
78577857

78587858
auto kpType = argType->castTo<BoundGenericType>();

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5598,7 +5598,7 @@ bool ConstraintSystem::repairFailures(
55985598
// fix-up here unless last component has already a invalid type or
55995599
// instance fix recorded.
56005600
if (isExpr<KeyPathExpr>(anchor)) {
5601-
if (isKnownKeyPathType(lhs) && isKnownKeyPathType(rhs)) {
5601+
if (lhs->isKnownKeyPathType() && rhs->isKnownKeyPathType()) {
56025602
// If we have a conversion happening here, we should let fix happen in
56035603
// simplifyRestrictedConstraint.
56045604
if (hasAnyRestriction())

lib/Sema/ConstraintSystem.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4363,12 +4363,6 @@ Solution::getFunctionArgApplyInfo(ConstraintLocator *locator) const {
43634363
fnInterfaceType, fnType, callee);
43644364
}
43654365

4366-
bool constraints::isKnownKeyPathType(Type type) {
4367-
return type->isKeyPath() || type->isWritableKeyPath() ||
4368-
type->isReferenceWritableKeyPath() || type->isPartialKeyPath() ||
4369-
type->isAnyKeyPath();
4370-
}
4371-
43724366
bool constraints::isTypeErasedKeyPathType(Type type) {
43734367
assert(type);
43744368

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "swift/AST/ClangModuleLoader.h"
3030
#include "swift/AST/DeclExportabilityVisitor.h"
3131
#include "swift/AST/DiagnosticsParse.h"
32+
#include "swift/AST/ExistentialLayout.h"
3233
#include "swift/AST/GenericEnvironment.h"
3334
#include "swift/AST/Initializer.h"
3435
#include "swift/AST/NameLookup.h"
@@ -2232,9 +2233,6 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
22322233
/// The context does not involve a key path access.
22332234
None,
22342235

2235-
/// The context is an expression that is coerced to a read-only key path.
2236-
ReadOnlyCoercion,
2237-
22382236
/// The context is a key path application (`x[keyPath: \.member]`).
22392237
Application,
22402238
};
@@ -2361,15 +2359,6 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
23612359
FCE->getLoc(),
23622360
Where.getDeclContext());
23632361
}
2364-
if (auto DTBE = dyn_cast<DerivedToBaseExpr>(E)) {
2365-
if (auto ty = DTBE->getType()) {
2366-
if (ty->isKeyPath()) {
2367-
walkInKeyPathAccessContext(DTBE->getSubExpr(),
2368-
KeyPathAccessContext::ReadOnlyCoercion);
2369-
return Action::SkipChildren(E);
2370-
}
2371-
}
2372-
}
23732362
if (auto KP = dyn_cast<KeyPathExpr>(E)) {
23742363
maybeDiagKeyPath(KP);
23752364
}
@@ -2586,19 +2575,49 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
25862575

25872576
StorageAccessKind getStorageAccessKindForKeyPath(KeyPathExpr *KP) const {
25882577
switch (KeyPathAccess) {
2589-
case KeyPathAccessContext::None:
2590-
// Use the key path's type to determine the access kind.
2591-
if (!KP->isObjC())
2592-
if (auto keyPathType = KP->getKeyPathType())
2593-
if (keyPathType->isKeyPath())
2578+
case KeyPathAccessContext::None: {
2579+
// Obj-C key paths are always considered mutable.
2580+
if (KP->isObjC())
2581+
return StorageAccessKind::GetSet;
2582+
2583+
// Check whether key path type indicates immutability, in which case only
2584+
// the getter will be used.
2585+
if (auto keyPathType = KP->getKeyPathType())
2586+
if (keyPathType->isKnownImmutableKeyPathType())
2587+
return StorageAccessKind::Get;
2588+
2589+
// Check if there's a conversion expression containing this one directly
2590+
// above it in the stack. If so, and that conversion is to an immutable
2591+
// key path type, then we should infer that use of the key path only
2592+
// implies use of the getter.
2593+
ArrayRef<const Expr *> exprs = ExprStack;
2594+
// Must be called while visiting the given key path expression.
2595+
ASSERT(exprs.back() == KP);
2596+
2597+
for (auto *expr : llvm::reverse(exprs.drop_back())) {
2598+
// Only traverse through conversions on the stack.
2599+
if (!isa<ImplicitConversionExpr>(expr) &&
2600+
!isa<OpenExistentialExpr>(expr))
2601+
break;
2602+
2603+
if (auto ty = expr->getType()) {
2604+
if (ty->isKnownImmutableKeyPathType())
25942605
return StorageAccessKind::Get;
25952606

2596-
return StorageAccessKind::GetSet;
2607+
if (auto existential = dyn_cast<ExistentialType>(ty)) {
2608+
if (auto superclass =
2609+
existential->getExistentialLayout().getSuperclass()) {
2610+
if (superclass->isKnownImmutableKeyPathType())
2611+
return StorageAccessKind::Get;
2612+
}
2613+
}
2614+
}
2615+
}
25972616

2598-
case KeyPathAccessContext::ReadOnlyCoercion:
2599-
// The type of this key path is being coerced to the type KeyPath<_, _> so
2600-
// ignore the actual key path type.
2601-
return StorageAccessKind::Get;
2617+
// We failed to prove that the key path is only used immutably,
2618+
// so diagnose both get and set access.
2619+
return StorageAccessKind::GetSet;
2620+
}
26022621

26032622
case KeyPathAccessContext::Application:
26042623
// The key path is being applied directly to a base so treat it as if it

0 commit comments

Comments
 (0)