Skip to content

Commit b29e8ba

Browse files
authored
Merge pull request #24052 from xedin/static-member-in-keypath-diags
[ConstraintSystem] Detect and fix use of static members in a key path in the solver
2 parents c1a21d2 + 55bc16e commit b29e8ba

File tree

10 files changed

+176
-22
lines changed

10 files changed

+176
-22
lines changed

lib/Sema/CSApply.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4572,10 +4572,8 @@ namespace {
45724572
}
45734573

45744574
// Key paths don't currently support static members.
4575-
if (varDecl->isStatic()) {
4576-
cs.TC.diagnose(componentLoc, diag::expr_keypath_static_member,
4577-
property->getFullName());
4578-
}
4575+
// There is a fix which diagnoses such situation already.
4576+
assert(!varDecl->isStatic());
45794577
}
45804578

45814579
cs.TC.requestMemberLayout(property);

lib/Sema/CSDiagnostics.cpp

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1748,12 +1748,16 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() {
17481748

17491749
Expr *expr = getParentExpr();
17501750
SourceRange baseRange = expr ? expr->getSourceRange() : SourceRange();
1751-
auto resolvedOverloadChoice = getResolvedOverload(locator)->Choice;
1751+
1752+
auto overload = getOverloadChoiceIfAvailable(locator);
1753+
if (!overload)
1754+
return false;
17521755

17531756
ValueDecl *decl = nullptr;
17541757

1755-
if (!resolvedOverloadChoice.isDecl()) {
1756-
if (auto MT = resolvedOverloadChoice.getBaseType()->getAs<MetatypeType>()) {
1758+
if (!overload->choice.isDecl()) {
1759+
auto baseTy = overload->choice.getBaseType();
1760+
if (auto MT = baseTy->getAs<MetatypeType>()) {
17571761
if (auto VD = dyn_cast<ValueDecl>(
17581762
MT->getMetatypeInstanceType()->getAnyNominal()->getAsDecl())) {
17591763
decl = VD;
@@ -1763,7 +1767,7 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() {
17631767
}
17641768
}
17651769

1766-
auto member = decl ? decl : resolvedOverloadChoice.getDecl();
1770+
auto member = decl ? decl : overload->choice.getDecl();
17671771

17681772
// If the base is an implicit self type reference, and we're in a
17691773
// an initializer, then the user wrote something like:
@@ -1933,16 +1937,24 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() {
19331937

19341938
return true;
19351939
}
1936-
1937-
if (isa<EnumElementDecl>(member)) {
1938-
Diag.emplace(emitDiagnostic(loc, diag::could_not_use_enum_element_on_instance,
1939-
Name));
1940+
1941+
// If this is a reference to a static member by one of the key path
1942+
// components, let's provide a tailored diagnostic and return because
1943+
// that is unsupported so there is no fix-it.
1944+
if (locator->isForKeyPathComponent()) {
1945+
InvalidStaticMemberRefInKeyPath failure(expr, getConstraintSystem(),
1946+
member, locator);
1947+
return failure.diagnoseAsError();
19401948
}
1941-
else {
1942-
Diag.emplace(emitDiagnostic(loc, diag::could_not_use_type_member_on_instance,
1943-
baseObjTy, Name));
1949+
1950+
if (isa<EnumElementDecl>(member)) {
1951+
Diag.emplace(emitDiagnostic(
1952+
loc, diag::could_not_use_enum_element_on_instance, Name));
1953+
} else {
1954+
Diag.emplace(emitDiagnostic(
1955+
loc, diag::could_not_use_type_member_on_instance, baseObjTy, Name));
19441956
}
1945-
1957+
19461958
Diag->highlight(getAnchor()->getSourceRange());
19471959

19481960
if (Name.isSimpleName(DeclBaseName::createConstructor()) &&
@@ -2002,7 +2014,7 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() {
20022014
}
20032015
}
20042016
}
2005-
2017+
20062018
// Fall back to a fix-it with a full type qualifier
20072019
auto nominal = member->getDeclContext()->getSelfNominalTypeDecl();
20082020
SmallString<32> typeName;
@@ -2436,3 +2448,22 @@ bool KeyPathSubscriptIndexHashableFailure::diagnoseAsError() {
24362448
resolveType(NonConformingType));
24372449
return true;
24382450
}
2451+
2452+
bool InvalidStaticMemberRefInKeyPath::diagnoseAsError() {
2453+
auto *anchor = getRawAnchor();
2454+
auto loc = anchor->getLoc();
2455+
2456+
if (auto *KPE = dyn_cast<KeyPathExpr>(anchor)) {
2457+
auto *locator = getLocator();
2458+
auto component =
2459+
llvm::find_if(locator->getPath(), [](const LocatorPathElt &elt) {
2460+
return elt.isKeyPathComponent();
2461+
});
2462+
2463+
assert(component != locator->getPath().end());
2464+
loc = KPE->getComponents()[component->getValue()].getLoc();
2465+
}
2466+
2467+
emitDiagnostic(loc, diag::expr_keypath_static_member, Member->getBaseName());
2468+
return true;
2469+
}

lib/Sema/CSDiagnostics.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,30 @@ class KeyPathSubscriptIndexHashableFailure final : public FailureDiagnostic {
10311031
bool diagnoseAsError() override;
10321032
};
10331033

1034+
/// Diagnose an attempt to reference a static member as a key path component
1035+
/// e.g.
1036+
///
1037+
/// ```swift
1038+
/// struct S {
1039+
/// static var foo: Int = 42
1040+
/// }
1041+
///
1042+
/// _ = \S.Type.foo
1043+
/// ```
1044+
class InvalidStaticMemberRefInKeyPath final : public FailureDiagnostic {
1045+
ValueDecl *Member;
1046+
1047+
public:
1048+
InvalidStaticMemberRefInKeyPath(Expr *root, ConstraintSystem &cs,
1049+
ValueDecl *member, ConstraintLocator *locator)
1050+
: FailureDiagnostic(root, cs, locator), Member(member) {
1051+
assert(member->hasName());
1052+
assert(locator->isForKeyPathComponent());
1053+
}
1054+
1055+
bool diagnoseAsError() override;
1056+
};
1057+
10341058
} // end namespace constraints
10351059
} // end namespace swift
10361060

lib/Sema/CSFix.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,3 +422,16 @@ TreatKeyPathSubscriptIndexAsHashable::create(ConstraintSystem &cs, Type type,
422422
return new (cs.getAllocator())
423423
TreatKeyPathSubscriptIndexAsHashable(cs, type, locator);
424424
}
425+
426+
bool AllowStaticMemberRefInKeyPath::diagnose(Expr *root, bool asNote) const {
427+
InvalidStaticMemberRefInKeyPath failure(root, getConstraintSystem(), Member,
428+
getLocator());
429+
return failure.diagnose(asNote);
430+
}
431+
432+
AllowStaticMemberRefInKeyPath *
433+
AllowStaticMemberRefInKeyPath::create(ConstraintSystem &cs, ValueDecl *member,
434+
ConstraintLocator *locator) {
435+
return new (cs.getAllocator())
436+
AllowStaticMemberRefInKeyPath(cs, member, locator);
437+
}

lib/Sema/CSFix.h

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,13 @@ enum class FixKind : uint8_t {
139139

140140
/// Allow KeyPaths to use AnyObject as root type
141141
AllowAnyObjectKeyPathRoot,
142-
142+
143143
/// Using subscript references in the keypath requires that each
144144
/// of the index arguments to be Hashable.
145145
TreatKeyPathSubscriptIndexAsHashable,
146+
147+
/// Allow a reference to a static member as a key path component.
148+
AllowStaticMemberRefInKeyPath,
146149
};
147150

148151
class ConstraintFix {
@@ -780,6 +783,25 @@ class TreatKeyPathSubscriptIndexAsHashable final : public ConstraintFix {
780783
create(ConstraintSystem &cs, Type type, ConstraintLocator *locator);
781784
};
782785

786+
class AllowStaticMemberRefInKeyPath final : public ConstraintFix {
787+
ValueDecl *Member;
788+
789+
AllowStaticMemberRefInKeyPath(ConstraintSystem &cs, ValueDecl *member,
790+
ConstraintLocator *locator)
791+
: ConstraintFix(cs, FixKind::AllowStaticMemberRefInKeyPath, locator),
792+
Member(member) {}
793+
794+
public:
795+
std::string getName() const override {
796+
return "allow reference to a static member as a key path component";
797+
}
798+
799+
bool diagnose(Expr *root, bool asNote = false) const override;
800+
801+
static AllowStaticMemberRefInKeyPath *
802+
create(ConstraintSystem &cs, ValueDecl *member, ConstraintLocator *locator);
803+
};
804+
783805
} // end namespace constraints
784806
} // end namespace swift
785807

lib/Sema/CSSimplify.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5015,11 +5015,26 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
50155015
if (!choices[i].isDecl()) {
50165016
return SolutionKind::Error;
50175017
}
5018+
50185019
auto storage = dyn_cast<AbstractStorageDecl>(choices[i].getDecl());
50195020
if (!storage) {
50205021
return SolutionKind::Error;
50215022
}
50225023

5024+
// Referencing static members in key path is not currently allowed.
5025+
if (storage->isStatic()) {
5026+
if (!shouldAttemptFixes())
5027+
return SolutionKind::Error;
5028+
5029+
auto componentLoc =
5030+
locator.withPathElement(LocatorPathElt::getKeyPathComponent(i));
5031+
auto *fix = AllowStaticMemberRefInKeyPath::create(
5032+
*this, choices[i].getDecl(), getConstraintLocator(componentLoc));
5033+
5034+
if (recordFix(fix))
5035+
return SolutionKind::Error;
5036+
}
5037+
50235038
if (isReadOnlyKeyPathComponent(storage)) {
50245039
capability = ReadOnly;
50255040
continue;
@@ -6314,6 +6329,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
63146329
case FixKind::AllowInaccessibleMember:
63156330
case FixKind::AllowAnyObjectKeyPathRoot:
63166331
case FixKind::TreatKeyPathSubscriptIndexAsHashable:
6332+
case FixKind::AllowStaticMemberRefInKeyPath:
63176333
llvm_unreachable("handled elsewhere");
63186334
}
63196335

lib/Sema/ConstraintLocator.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ bool ConstraintLocator::isKeyPathSubscriptComponent() const {
147147
});
148148
}
149149

150+
bool ConstraintLocator::isForKeyPathComponent() const {
151+
return llvm::any_of(getPath(), [&](const LocatorPathElt &elt) {
152+
return elt.isKeyPathComponent();
153+
});
154+
}
155+
150156
void ConstraintLocator::dump(SourceManager *sm) {
151157
dump(sm, llvm::errs());
152158
llvm::errs() << "\n";

lib/Sema/ConstraintLocator.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,10 +540,14 @@ class ConstraintLocator : public llvm::FoldingSetNode {
540540
/// as result of the key path dynamic member lookup operation.
541541
bool isResultOfKeyPathDynamicMemberLookup() const;
542542

543-
/// Determine whether given locator points to a subscript component
543+
/// Determine whether this locator points to a subscript component
544544
/// of the key path at some index.
545545
bool isKeyPathSubscriptComponent() const;
546546

547+
/// Determine whether this locator points to one of the key path
548+
/// components.
549+
bool isForKeyPathComponent() const;
550+
547551
/// Produce a profile of this locator, for use in a folding set.
548552
static void Profile(llvm::FoldingSetNodeID &id, Expr *anchor,
549553
ArrayRef<PathElement> path);

test/SILOptimizer/access_wmo_diagnose.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ public class C {
1010
}
1111

1212
public func testGlobalProp() {
13-
let a: AnyKeyPath = \C.globalProp // expected-error{{static member 'globalProp' cannot be used on instance of type 'C'}}
13+
let a: AnyKeyPath = \C.globalProp // expected-error{{key path cannot refer to static member 'globalProp'}}
1414
}

test/expr/unary/keypath/keypath.swift

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -514,9 +514,9 @@ class X {
514514
}
515515

516516
func testStaticKeyPathComponent() {
517-
_ = \X.a // expected-error{{}}
517+
_ = \X.a // expected-error{{cannot refer to static member}}
518518
_ = \X.Type.a // expected-error{{cannot refer to static member}}
519-
_ = \X.b // expected-error{{}}
519+
_ = \X.b // expected-error{{cannot refer to static member}}
520520
_ = \X.Type.b // expected-error{{cannot refer to static member}}
521521
}
522522

@@ -708,6 +708,46 @@ var identity11: AnyKeyPath = \Container.self
708708

709709
var interleavedIdentityComponents = \Container.self.base.self?.self.i.self
710710

711+
protocol P_With_Static_Members {
712+
static var x: Int { get }
713+
static var arr: [Int] { get }
714+
}
715+
716+
func test_keypath_with_static_members(_ p: P_With_Static_Members) {
717+
let _ = p[keyPath: \.x]
718+
// expected-error@-1 {{key path cannot refer to static member 'x'}}
719+
let _: KeyPath<P_With_Static_Members, Int> = \.x
720+
// expected-error@-1 {{key path cannot refer to static member 'x'}}
721+
let _ = \P_With_Static_Members.arr.count
722+
// expected-error@-1 {{key path cannot refer to static member 'arr'}}
723+
let _ = p[keyPath: \.arr.count]
724+
// expected-error@-1 {{key path cannot refer to static member 'arr'}}
725+
726+
struct S {
727+
static var foo: String = "Hello"
728+
var bar: Bar
729+
}
730+
731+
struct Bar {
732+
static var baz: Int = 42
733+
}
734+
735+
func foo(_ s: S) {
736+
let _ = \S.Type.foo
737+
// expected-error@-1 {{key path cannot refer to static member 'foo'}}
738+
let _ = s[keyPath: \.foo]
739+
// expected-error@-1 {{key path cannot refer to static member 'foo'}}
740+
let _: KeyPath<S, String> = \.foo
741+
// expected-error@-1 {{key path cannot refer to static member 'foo'}}
742+
let _ = \S.foo
743+
// expected-error@-1 {{key path cannot refer to static member 'foo'}}
744+
let _ = \S.bar.baz
745+
// expected-error@-1 {{key path cannot refer to static member 'baz'}}
746+
let _ = s[keyPath: \.bar.baz]
747+
// expected-error@-1 {{key path cannot refer to static member 'baz'}}
748+
}
749+
}
750+
711751
func testSyntaxErrors() { // expected-note{{}}
712752
_ = \. ; // expected-error{{expected member name following '.'}}
713753
_ = \.a ;

0 commit comments

Comments
 (0)