Skip to content

Commit 072e84a

Browse files
theblixguyxedin
authored andcommitted
[CSSimplify] Reject key path if root type is AnyObject (#23820)
Detect situations where `AnyObject` is attempted to be used as a root type of the key path early and diagnose via new diagnostics framework.
1 parent 9647c2f commit 072e84a

File tree

13 files changed

+179
-22
lines changed

13 files changed

+179
-22
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,8 @@ ERROR(expr_keypath_subscript_index_not_hashable, none,
506506
ERROR(expr_smart_keypath_application_type_mismatch,none,
507507
"key path of type %0 cannot be applied to a base of type %1",
508508
(Type, Type))
509+
ERROR(expr_swift_keypath_anyobject_root,none,
510+
"the root type of a Swift key path cannot be 'AnyObject'", ())
509511
WARNING(expr_deprecated_writable_keypath,none,
510512
"forming a writable keypath to property %0 that is read-only in this context "
511513
"is deprecated and will be removed in a future release",(DeclName))

lib/Sema/CSApply.cpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4342,15 +4342,6 @@ namespace {
43424342
Type baseTy = keyPathTy->getGenericArgs()[0];
43434343
Type leafTy = keyPathTy->getGenericArgs()[1];
43444344

4345-
// We do not allow keypaths to go through AnyObject
4346-
if (baseTy->isAnyObject()) {
4347-
auto rootTyRepr = E->getRootType();
4348-
cs.TC.diagnose(rootTyRepr->getLoc(),
4349-
diag::expr_swift_keypath_invalid_component)
4350-
.highlight(rootTyRepr->getSourceRange());
4351-
return nullptr;
4352-
}
4353-
43544345
for (unsigned i : indices(E->getComponents())) {
43554346
auto &origComponent = E->getMutableComponents()[i];
43564347

lib/Sema/CSDiag.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,7 @@ void FailureDiagnosis::diagnoseUnviableLookupResults(
758758
case MemberLookupResult::UR_LabelMismatch:
759759
case MemberLookupResult::UR_WritableKeyPathOnReadOnlyMember:
760760
case MemberLookupResult::UR_ReferenceWritableKeyPathOnMutatingMember:
761+
case MemberLookupResult::UR_KeyPathWithAnyObjectRootType:
761762
break;
762763
case MemberLookupResult::UR_UnavailableInExistential:
763764
diagnose(loc, diag::could_not_use_member_on_existential,

lib/Sema/CSDiagnostics.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2399,6 +2399,24 @@ bool InaccessibleMemberFailure::diagnoseAsError() {
23992399
return true;
24002400
}
24012401

2402+
bool AnyObjectKeyPathRootFailure::diagnoseAsError() {
2403+
// Diagnose use of AnyObject as root for a keypath
2404+
2405+
auto anchor = getAnchor();
2406+
auto loc = anchor->getLoc();
2407+
auto range = anchor->getSourceRange();
2408+
2409+
if (auto KPE = dyn_cast<KeyPathExpr>(anchor)) {
2410+
if (auto rootTyRepr = KPE->getRootType()) {
2411+
loc = rootTyRepr->getLoc();
2412+
range = rootTyRepr->getSourceRange();
2413+
}
2414+
}
2415+
2416+
emitDiagnostic(loc, diag::expr_swift_keypath_anyobject_root).highlight(range);
2417+
return true;
2418+
}
2419+
24022420
bool KeyPathSubscriptIndexHashableFailure::diagnoseAsError() {
24032421
auto *anchor = getRawAnchor();
24042422
auto *locator = getLocator();

lib/Sema/CSDiagnostics.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,22 @@ class InaccessibleMemberFailure final : public FailureDiagnostic {
987987
bool diagnoseAsError() override;
988988
};
989989

990+
991+
// Diagnose an attempt to use AnyObject as the root type of a KeyPath
992+
//
993+
// ```swift
994+
// let keyPath = \AnyObject.bar
995+
// ```
996+
class AnyObjectKeyPathRootFailure final : public FailureDiagnostic {
997+
998+
public:
999+
AnyObjectKeyPathRootFailure(Expr *root, ConstraintSystem &cs,
1000+
ConstraintLocator *locator)
1001+
: FailureDiagnostic(root, cs, locator) {}
1002+
1003+
bool diagnoseAsError() override;
1004+
};
1005+
9901006
/// Diagnose an attempt to reference subscript as a keypath component
9911007
/// where at least one of the index arguments doesn't conform to Hashable e.g.
9921008
///

lib/Sema/CSFix.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,18 @@ AllowInaccessibleMember::create(ConstraintSystem &cs, ValueDecl *member,
397397
return new (cs.getAllocator()) AllowInaccessibleMember(cs, member, locator);
398398
}
399399

400+
bool AllowAnyObjectKeyPathRoot::diagnose(Expr *root, bool asNote) const {
401+
AnyObjectKeyPathRootFailure failure(root, getConstraintSystem(),
402+
getLocator());
403+
return failure.diagnose(asNote);
404+
}
405+
406+
AllowAnyObjectKeyPathRoot *
407+
AllowAnyObjectKeyPathRoot::create(ConstraintSystem &cs,
408+
ConstraintLocator *locator) {
409+
return new (cs.getAllocator()) AllowAnyObjectKeyPathRoot(cs, locator);
410+
}
411+
400412
bool TreatKeyPathSubscriptIndexAsHashable::diagnose(Expr *root,
401413
bool asNote) const {
402414
KeyPathSubscriptIndexHashableFailure failure(root, getConstraintSystem(),

lib/Sema/CSFix.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,11 @@ enum class FixKind : uint8_t {
137137
/// no access control.
138138
AllowInaccessibleMember,
139139

140+
/// Allow KeyPaths to use AnyObject as root type
141+
AllowAnyObjectKeyPathRoot,
142+
140143
/// Using subscript references in the keypath requires that each
141-
// of the index arguments to be Hashable.
144+
/// of the index arguments to be Hashable.
142145
TreatKeyPathSubscriptIndexAsHashable,
143146
};
144147

@@ -741,6 +744,22 @@ class AllowInaccessibleMember final : public ConstraintFix {
741744
ConstraintLocator *locator);
742745
};
743746

747+
class AllowAnyObjectKeyPathRoot final : public ConstraintFix {
748+
749+
AllowAnyObjectKeyPathRoot(ConstraintSystem &cs, ConstraintLocator *locator)
750+
: ConstraintFix(cs, FixKind::AllowAnyObjectKeyPathRoot, locator) {}
751+
752+
public:
753+
std::string getName() const override {
754+
return "allow anyobject as root type for a keypath";
755+
}
756+
757+
bool diagnose(Expr *root, bool asNote = false) const override;
758+
759+
static AllowAnyObjectKeyPathRoot *create(ConstraintSystem &cs,
760+
ConstraintLocator *locator);
761+
};
762+
744763
class TreatKeyPathSubscriptIndexAsHashable final : public ConstraintFix {
745764
Type NonConformingType;
746765

lib/Sema/CSGen.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2907,8 +2907,10 @@ namespace {
29072907

29082908
// For native key paths, traverse the key path components to set up
29092909
// appropriate type relationships at each level.
2910+
auto rootLocator =
2911+
CS.getConstraintLocator(E, ConstraintLocator::KeyPathRoot);
29102912
auto locator = CS.getConstraintLocator(E);
2911-
Type root = CS.createTypeVariable(locator);
2913+
Type root = CS.createTypeVariable(rootLocator);
29122914

29132915
// If a root type was explicitly given, then resolve it now.
29142916
if (auto rootRepr = E->getRootType()) {
@@ -3015,7 +3017,9 @@ namespace {
30153017
base = optTy;
30163018
}
30173019

3018-
auto rvalueBase = CS.createTypeVariable(locator);
3020+
auto baseLocator =
3021+
CS.getConstraintLocator(E, ConstraintLocator::KeyPathValue);
3022+
auto rvalueBase = CS.createTypeVariable(baseLocator);
30193023
CS.addConstraint(ConstraintKind::Equal, base, rvalueBase, locator);
30203024

30213025
// The result is a KeyPath from the root to the end component.

lib/Sema/CSSimplify.cpp

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1851,6 +1851,22 @@ ConstraintSystem::matchTypesBindTypeVar(
18511851
}
18521852
}
18531853

1854+
// We do not allow keypaths to go through AnyObject. Let's create a fix
1855+
// so this can be diagnosed later.
1856+
if (auto loc = typeVar->getImpl().getLocator()) {
1857+
auto locPath = loc->getPath();
1858+
1859+
if (!locPath.empty() &&
1860+
locPath.back().getKind() == ConstraintLocator::KeyPathRoot &&
1861+
type->isAnyObject()) {
1862+
auto *fix = AllowAnyObjectKeyPathRoot::create(
1863+
*this, getConstraintLocator(locator));
1864+
1865+
if (recordFix(fix))
1866+
return getTypeMatchFailure(locator);
1867+
}
1868+
}
1869+
18541870
// Okay. Bind below.
18551871

18561872
// A constraint that binds any pointer to a void pointer is
@@ -3591,8 +3607,14 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
35913607
if (memberName.isSimpleName() &&
35923608
memberName.getBaseName().getKind() == DeclBaseName::Kind::Subscript &&
35933609
!isKeyPathDynamicMemberLookup(memberLocator)) {
3594-
result.ViableCandidates.push_back(
3595-
OverloadChoice(baseTy, OverloadChoiceKind::KeyPathApplication));
3610+
if (baseTy->isAnyObject()) {
3611+
result.addUnviable(
3612+
OverloadChoice(baseTy, OverloadChoiceKind::KeyPathApplication),
3613+
MemberLookupResult::UR_KeyPathWithAnyObjectRootType);
3614+
} else {
3615+
result.ViableCandidates.push_back(
3616+
OverloadChoice(baseTy, OverloadChoiceKind::KeyPathApplication));
3617+
}
35963618
}
35973619

35983620
// If the base type is a tuple type, look for the named or indexed member
@@ -4273,6 +4295,8 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
42734295
case MemberLookupResult::UR_WritableKeyPathOnReadOnlyMember:
42744296
case MemberLookupResult::UR_ReferenceWritableKeyPathOnMutatingMember:
42754297
break;
4298+
case MemberLookupResult::UR_KeyPathWithAnyObjectRootType:
4299+
return AllowAnyObjectKeyPathRoot::create(cs, locator);
42764300
}
42774301
}
42784302

@@ -4950,7 +4974,7 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
49504974
return SolutionKind::Error;
49514975
}
49524976
}
4953-
4977+
49544978
// See if we resolved overloads for all the components involved.
49554979
enum {
49564980
ReadOnly,
@@ -5085,7 +5109,7 @@ ConstraintSystem::simplifyKeyPathApplicationConstraint(
50855109
}
50865110
return SolutionKind::Unsolved;
50875111
};
5088-
5112+
50895113
if (auto clas = keyPathTy->getAs<NominalType>()) {
50905114
if (clas->getDecl() == getASTContext().getAnyKeyPathDecl()) {
50915115
// Read-only keypath, whose projected value is upcast to `Any?`.
@@ -6288,6 +6312,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
62886312
case FixKind::AllowClosureParameterDestructuring:
62896313
case FixKind::MoveOutOfOrderArgument:
62906314
case FixKind::AllowInaccessibleMember:
6315+
case FixKind::AllowAnyObjectKeyPathRoot:
62916316
case FixKind::TreatKeyPathSubscriptIndexAsHashable:
62926317
llvm_unreachable("handled elsewhere");
62936318
}

lib/Sema/ConstraintLocator.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor,
7979
case DynamicLookupResult:
8080
case ContextualType:
8181
case SynthesizedArgument:
82+
case KeyPathRoot:
83+
case KeyPathValue:
8284
if (unsigned numValues = numNumericValuesInPathElement(elt.getKind())) {
8385
id.AddInteger(elt.getValue());
8486
if (numValues > 1)
@@ -101,6 +103,26 @@ bool ConstraintLocator::isSubscriptMemberRef() const {
101103
return path.back().getKind() == ConstraintLocator::SubscriptMember;
102104
}
103105

106+
bool ConstraintLocator::isKeyPathRoot() const {
107+
auto *anchor = getAnchor();
108+
auto path = getPath();
109+
110+
if (!anchor || path.empty())
111+
return false;
112+
113+
return path.back().getKind() == ConstraintLocator::KeyPathRoot;
114+
}
115+
116+
bool ConstraintLocator::isKeyPathValue() const {
117+
auto *anchor = getAnchor();
118+
auto path = getPath();
119+
120+
if (!anchor || path.empty())
121+
return false;
122+
123+
return path.back().getKind() == ConstraintLocator::KeyPathValue;
124+
}
125+
104126
bool ConstraintLocator::isResultOfKeyPathDynamicMemberLookup() const {
105127
return llvm::any_of(getPath(), [](const LocatorPathElt &elt) {
106128
return elt.isKeyPathDynamicMember();
@@ -310,6 +332,14 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) {
310332
case KeyPathDynamicMember:
311333
out << " keypath dynamic member lookup";
312334
break;
335+
336+
case KeyPathRoot:
337+
out << " keypath root";
338+
break;
339+
340+
case KeyPathValue:
341+
out << " keypath value";
342+
break;
313343
}
314344
}
315345

lib/Sema/ConstraintLocator.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ class ConstraintLocator : public llvm::FoldingSetNode {
129129
SynthesizedArgument,
130130
/// The member looked up via keypath based dynamic lookup.
131131
KeyPathDynamicMember,
132+
/// The root of a keypath
133+
KeyPathRoot,
134+
/// The value of a keypath
135+
KeyPathValue,
132136
};
133137

134138
/// Determine the number of numeric values used for the given path
@@ -159,6 +163,8 @@ class ConstraintLocator : public llvm::FoldingSetNode {
159163
case ImplicitlyUnwrappedDisjunctionChoice:
160164
case DynamicLookupResult:
161165
case ContextualType:
166+
case KeyPathRoot:
167+
case KeyPathValue:
162168
return 0;
163169

164170
case OpenedGeneric:
@@ -225,6 +231,8 @@ class ConstraintLocator : public llvm::FoldingSetNode {
225231
case ContextualType:
226232
case SynthesizedArgument:
227233
case KeyPathDynamicMember:
234+
case KeyPathRoot:
235+
case KeyPathValue:
228236
return 0;
229237

230238
case FunctionArgument:
@@ -522,6 +530,12 @@ class ConstraintLocator : public llvm::FoldingSetNode {
522530
/// e.g. `foo[0]` or `\Foo.[0]`
523531
bool isSubscriptMemberRef() const;
524532

533+
/// Determine whether given locator points to the keypath root
534+
bool isKeyPathRoot() const;
535+
536+
/// Determine whether given locator points to the keypath value
537+
bool isKeyPathValue() const;
538+
525539
/// Determine whether given locator points to the choice picked as
526540
/// as result of the key path dynamic member lookup operation.
527541
bool isResultOfKeyPathDynamicMemberLookup() const;

lib/Sema/ConstraintSystem.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -855,25 +855,25 @@ struct MemberLookupResult {
855855
enum UnviableReason {
856856
/// Argument labels don't match.
857857
UR_LabelMismatch,
858-
858+
859859
/// This uses a type like Self in its signature that cannot be used on an
860860
/// existential box.
861861
UR_UnavailableInExistential,
862-
862+
863863
/// This is an instance member being accessed through something of metatype
864864
/// type.
865865
UR_InstanceMemberOnType,
866-
866+
867867
/// This is a static/class member being accessed through an instance.
868868
UR_TypeMemberOnInstance,
869-
869+
870870
/// This is a mutating member, being used on an rvalue.
871871
UR_MutatingMemberOnRValue,
872-
872+
873873
/// The getter for this subscript or computed property is mutating and we
874874
/// only have an rvalue base. This is more specific than the former one.
875875
UR_MutatingGetterOnRValue,
876-
876+
877877
/// The member is inaccessible (e.g. a private member in another file).
878878
UR_Inaccessible,
879879

@@ -882,11 +882,15 @@ struct MemberLookupResult {
882882
/// because it's not known upfront what access capability would the
883883
/// member have.
884884
UR_WritableKeyPathOnReadOnlyMember,
885+
885886
/// This is a `ReferenceWritableKeyPath` being used to look up mutating
886887
/// member, used in situations involving dynamic member lookup via keypath,
887888
/// because it's not known upfront what access capability would the
888889
/// member have.
889890
UR_ReferenceWritableKeyPathOnMutatingMember,
891+
892+
/// This is a KeyPath whose root type is AnyObject
893+
UR_KeyPathWithAnyObjectRootType
890894
};
891895

892896
/// This is a list of considered (but rejected) candidates, along with a

test/expr/primary/keypath/keypath-objc.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,24 @@ func testParseErrors() {
143143
func testTypoCorrection() {
144144
let _: String = #keyPath(A.proString) // expected-error {{type 'A' has no member 'proString'}}
145145
}
146+
147+
class SR_10146_1 {
148+
@objc let b = 1
149+
}
150+
151+
class SR_10146_2: SR_10146_1 {
152+
let a = \AnyObject.b // expected-error {{the root type of a Swift key path cannot be 'AnyObject'}}
153+
}
154+
155+
class SR_10146_3 {
156+
@objc let abc: Int = 1
157+
158+
func doNotCrash() {
159+
let _: KeyPath<AnyObject, Int> = \.abc // expected-error {{the root type of a Swift key path cannot be 'AnyObject'}}
160+
}
161+
162+
func doNotCrash_1(_ obj: AnyObject, _ kp: KeyPath<AnyObject, Int>) {
163+
let _ = obj[keyPath: \.abc] // expected-error 2{{the root type of a Swift key path cannot be 'AnyObject'}}
164+
let _ = obj[keyPath: kp] // expected-error {{the root type of a Swift key path cannot be 'AnyObject'}}
165+
}
166+
}

0 commit comments

Comments
 (0)