Skip to content

Commit 9740a31

Browse files
authored
Merge pull request #24026 from xedin/keypath-anyobject-root-via-fixes-5.1
[5.1][CSSimplify] Reject key path if root type is AnyObject
2 parents ad3e6c7 + 4cbf09e commit 9740a31

File tree

13 files changed

+189
-22
lines changed

13 files changed

+189
-22
lines changed

include/swift/AST/DiagnosticsSema.def

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

lib/Sema/CSApply.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4379,7 +4379,7 @@ namespace {
43794379
auto keyPathTy = cs.getType(E)->castTo<BoundGenericType>();
43804380
Type baseTy = keyPathTy->getGenericArgs()[0];
43814381
Type leafTy = keyPathTy->getGenericArgs()[1];
4382-
4382+
43834383
for (unsigned i : indices(E->getComponents())) {
43844384
auto &origComponent = E->getMutableComponents()[i];
43854385

lib/Sema/CSDiag.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,7 @@ void FailureDiagnosis::diagnoseUnviableLookupResults(
813813
case MemberLookupResult::UR_LabelMismatch:
814814
case MemberLookupResult::UR_WritableKeyPathOnReadOnlyMember:
815815
case MemberLookupResult::UR_ReferenceWritableKeyPathOnMutatingMember:
816+
case MemberLookupResult::UR_KeyPathWithAnyObjectRootType:
816817
break;
817818
case MemberLookupResult::UR_UnavailableInExistential:
818819
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
@@ -2398,6 +2398,24 @@ bool InaccessibleMemberFailure::diagnoseAsError() {
23982398
return true;
23992399
}
24002400

2401+
bool AnyObjectKeyPathRootFailure::diagnoseAsError() {
2402+
// Diagnose use of AnyObject as root for a keypath
2403+
2404+
auto anchor = getAnchor();
2405+
auto loc = anchor->getLoc();
2406+
auto range = anchor->getSourceRange();
2407+
2408+
if (auto KPE = dyn_cast<KeyPathExpr>(anchor)) {
2409+
if (auto rootTyRepr = KPE->getRootType()) {
2410+
loc = rootTyRepr->getLoc();
2411+
range = rootTyRepr->getSourceRange();
2412+
}
2413+
}
2414+
2415+
emitDiagnostic(loc, diag::expr_swift_keypath_anyobject_root).highlight(range);
2416+
return true;
2417+
}
2418+
24012419
bool KeyPathSubscriptIndexHashableFailure::diagnoseAsError() {
24022420
auto *anchor = getRawAnchor();
24032421
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
@@ -2902,8 +2902,10 @@ namespace {
29022902

29032903
// For native key paths, traverse the key path components to set up
29042904
// appropriate type relationships at each level.
2905+
auto rootLocator =
2906+
CS.getConstraintLocator(E, ConstraintLocator::KeyPathRoot);
29052907
auto locator = CS.getConstraintLocator(E);
2906-
Type root = CS.createTypeVariable(locator);
2908+
Type root = CS.createTypeVariable(rootLocator);
29072909

29082910
// If a root type was explicitly given, then resolve it now.
29092911
if (auto rootRepr = E->getRootType()) {
@@ -3010,7 +3012,9 @@ namespace {
30103012
base = optTy;
30113013
}
30123014

3013-
auto rvalueBase = CS.createTypeVariable(locator);
3015+
auto baseLocator =
3016+
CS.getConstraintLocator(E, ConstraintLocator::KeyPathValue);
3017+
auto rvalueBase = CS.createTypeVariable(baseLocator);
30143018
CS.addConstraint(ConstraintKind::Equal, base, rvalueBase, locator);
30153019

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

lib/Sema/CSSimplify.cpp

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1845,6 +1845,22 @@ ConstraintSystem::matchTypesBindTypeVar(
18451845
}
18461846
}
18471847

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

18501866
// A constraint that binds any pointer to a void pointer is
@@ -3585,8 +3601,14 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
35853601
if (memberName.isSimpleName() &&
35863602
memberName.getBaseName().getKind() == DeclBaseName::Kind::Subscript &&
35873603
!isKeyPathDynamicMemberLookup(memberLocator)) {
3588-
result.ViableCandidates.push_back(
3589-
OverloadChoice(baseTy, OverloadChoiceKind::KeyPathApplication));
3604+
if (baseTy->isAnyObject()) {
3605+
result.addUnviable(
3606+
OverloadChoice(baseTy, OverloadChoiceKind::KeyPathApplication),
3607+
MemberLookupResult::UR_KeyPathWithAnyObjectRootType);
3608+
} else {
3609+
result.ViableCandidates.push_back(
3610+
OverloadChoice(baseTy, OverloadChoiceKind::KeyPathApplication));
3611+
}
35903612
}
35913613

35923614
// If the base type is a tuple type, look for the named or indexed member
@@ -4237,13 +4259,13 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
42374259
DeclName memberName, const OverloadChoice &choice,
42384260
ConstraintLocator *locator,
42394261
Optional<MemberLookupResult::UnviableReason> reason = None) {
4240-
if (!choice.isDecl())
4241-
return nullptr;
4242-
4243-
auto *decl = choice.getDecl();
4244-
if (auto *CD = dyn_cast<ConstructorDecl>(decl)) {
4245-
if (auto *fix = validateInitializerRef(cs, CD, locator))
4246-
return fix;
4262+
// Not all of the choices handled here are going
4263+
// to refer to a declaration.
4264+
if (choice.isDecl()) {
4265+
if (auto *CD = dyn_cast<ConstructorDecl>(choice.getDecl())) {
4266+
if (auto *fix = validateInitializerRef(cs, CD, locator))
4267+
return fix;
4268+
}
42474269
}
42484270

42494271
if (reason) {
@@ -4253,7 +4275,8 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
42534275
return AllowTypeOrInstanceMember::create(cs, baseTy, memberName, locator);
42544276

42554277
case MemberLookupResult::UR_Inaccessible:
4256-
return AllowInaccessibleMember::create(cs, decl, locator);
4278+
assert(choice.isDecl());
4279+
return AllowInaccessibleMember::create(cs, choice.getDecl(), locator);
42574280

42584281
case MemberLookupResult::UR_MutatingMemberOnRValue:
42594282
case MemberLookupResult::UR_MutatingGetterOnRValue:
@@ -4266,6 +4289,8 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
42664289
case MemberLookupResult::UR_WritableKeyPathOnReadOnlyMember:
42674290
case MemberLookupResult::UR_ReferenceWritableKeyPathOnMutatingMember:
42684291
break;
4292+
case MemberLookupResult::UR_KeyPathWithAnyObjectRootType:
4293+
return AllowAnyObjectKeyPathRoot::create(cs, locator);
42694294
}
42704295
}
42714296

@@ -4943,7 +4968,7 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
49434968
return SolutionKind::Error;
49444969
}
49454970
}
4946-
4971+
49474972
// See if we resolved overloads for all the components involved.
49484973
enum {
49494974
ReadOnly,
@@ -5078,7 +5103,7 @@ ConstraintSystem::simplifyKeyPathApplicationConstraint(
50785103
}
50795104
return SolutionKind::Unsolved;
50805105
};
5081-
5106+
50825107
if (auto clas = keyPathTy->getAs<NominalType>()) {
50835108
if (clas->getDecl() == getASTContext().getAnyKeyPathDecl()) {
50845109
// Read-only keypath, whose projected value is upcast to `Any?`.
@@ -6281,6 +6306,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
62816306
case FixKind::AllowClosureParameterDestructuring:
62826307
case FixKind::MoveOutOfOrderArgument:
62836308
case FixKind::AllowInaccessibleMember:
6309+
case FixKind::AllowAnyObjectKeyPathRoot:
62846310
case FixKind::TreatKeyPathSubscriptIndexAsHashable:
62856311
llvm_unreachable("handled elsewhere");
62866312
}

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

0 commit comments

Comments
 (0)