Skip to content

Commit 072e84a

Browse files
theblixguyxedin
authored andcommitted
[CSSimplify] Reject key path if root type is AnyObject (swiftlang#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

0 commit comments

Comments
 (0)