|
29 | 29 | #include "swift/AST/ClangModuleLoader.h"
|
30 | 30 | #include "swift/AST/DeclExportabilityVisitor.h"
|
31 | 31 | #include "swift/AST/DiagnosticsParse.h"
|
| 32 | +#include "swift/AST/ExistentialLayout.h" |
32 | 33 | #include "swift/AST/GenericEnvironment.h"
|
33 | 34 | #include "swift/AST/Initializer.h"
|
34 | 35 | #include "swift/AST/NameLookup.h"
|
@@ -2232,9 +2233,6 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
|
2232 | 2233 | /// The context does not involve a key path access.
|
2233 | 2234 | None,
|
2234 | 2235 |
|
2235 |
| - /// The context is an expression that is coerced to a read-only key path. |
2236 |
| - ReadOnlyCoercion, |
2237 |
| - |
2238 | 2236 | /// The context is a key path application (`x[keyPath: \.member]`).
|
2239 | 2237 | Application,
|
2240 | 2238 | };
|
@@ -2361,15 +2359,6 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
|
2361 | 2359 | FCE->getLoc(),
|
2362 | 2360 | Where.getDeclContext());
|
2363 | 2361 | }
|
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 |
| - } |
2373 | 2362 | if (auto KP = dyn_cast<KeyPathExpr>(E)) {
|
2374 | 2363 | maybeDiagKeyPath(KP);
|
2375 | 2364 | }
|
@@ -2586,19 +2575,49 @@ class ExprAvailabilityWalker : public BaseDiagnosticWalker {
|
2586 | 2575 |
|
2587 | 2576 | StorageAccessKind getStorageAccessKindForKeyPath(KeyPathExpr *KP) const {
|
2588 | 2577 | 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()) |
2594 | 2605 | return StorageAccessKind::Get;
|
2595 | 2606 |
|
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 | + } |
2597 | 2616 |
|
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 | + } |
2602 | 2621 |
|
2603 | 2622 | case KeyPathAccessContext::Application:
|
2604 | 2623 | // The key path is being applied directly to a base so treat it as if it
|
|
0 commit comments