Skip to content

[CSDiagnostics] Prevent nested type references in KeyPath components #83625

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,9 @@ ERROR(expr_keypath_nonescapable_type,none,
ERROR(expr_keypath_not_property,none,
"%select{key path|dynamic key path member lookup}1 cannot refer to %kind0",
(const ValueDecl *, bool))
ERROR(expr_keypath_type_ref,none,
"%select{key path|dynamic key path member lookup}1 cannot refer to type %0",
(const ValueDecl *, bool))
ERROR(expr_keypath_mutating_getter,none,
"%select{key path|dynamic key path member lookup}1 cannot refer to %0, "
"which has a mutating getter",
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Sema/CSFix.h
Original file line number Diff line number Diff line change
Expand Up @@ -2024,6 +2024,8 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix {
AsyncOrThrowsMethod,
// Allow a reference to an enum case as a key path component.
EnumCase,
// Allow a reference to a type as a key path component.
TypeReference,
} Kind;

ValueDecl *Member;
Expand Down Expand Up @@ -2056,6 +2058,8 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix {
case RefKind::AsyncOrThrowsMethod:
return "allow reference to async or throwing method as a key path "
"component";
case RefKind::TypeReference:
return "allow reference to a type as a key path component";
}
llvm_unreachable("covered switch");
}
Expand Down
6 changes: 6 additions & 0 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6485,6 +6485,12 @@ bool InvalidAsyncOrThrowsMethodRefInKeyPath::diagnoseAsError() {
return true;
}

bool InvalidTypeRefInKeyPath::diagnoseAsError() {
emitDiagnostic(diag::expr_keypath_type_ref, getMember(),
isForKeyPathDynamicMemberLookup());
return true;
}

SourceLoc InvalidUseOfAddressOf::getLoc() const {
auto anchor = getAnchor();

Expand Down
21 changes: 21 additions & 0 deletions lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -1889,6 +1889,27 @@ class InvalidAsyncOrThrowsMethodRefInKeyPath final
bool diagnoseAsError() override;
};

/// Diagnose an attempt to reference a type as a key path component
/// e.g.
///
/// ```swift
/// struct S {
/// enum Q {}
/// }
///
/// _ = \S.Type.Q
/// ```
class InvalidTypeRefInKeyPath final : public InvalidMemberRefInKeyPath {
public:
InvalidTypeRefInKeyPath(const Solution &solution, ValueDecl *member,
ConstraintLocator *locator)
: InvalidMemberRefInKeyPath(solution, member, locator) {
assert(isa<TypeDecl>(member));
}

bool diagnoseAsError() override;
};

/// Diagnose an attempt to reference a member which has a mutating getter as a
/// key path component e.g.
///
Expand Down
9 changes: 9 additions & 0 deletions lib/Sema/CSFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1288,6 +1288,10 @@ bool AllowInvalidRefInKeyPath::diagnose(const Solution &solution,
getLocator());
return failure.diagnose(asNote);
}
case RefKind::TypeReference: {
InvalidTypeRefInKeyPath failure(solution, Member, getLocator());
return failure.diagnose(asNote);
}
}
llvm_unreachable("covered switch");
}
Expand Down Expand Up @@ -1386,6 +1390,11 @@ AllowInvalidRefInKeyPath::forRef(ConstraintSystem &cs, Type baseType,
return AllowInvalidRefInKeyPath::create(cs, baseType, RefKind::Initializer,
member, locator);

// Referencing types in key path is not currently allowed.
if (isa<TypeDecl>(member))
return AllowInvalidRefInKeyPath::create(cs, baseType, RefKind::TypeReference,
member, locator);

return nullptr;
}

Expand Down
11 changes: 11 additions & 0 deletions test/expr/unary/keypath/keypath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1429,3 +1429,14 @@ func testKeyPathInout() {
takesInoutOpt(\.count)
takesInoutOpt(\String.count)
}

func testKeypathWithTypeReference() {
struct S {
enum Q {
static let i = 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the indentation is off here a bit, we use 2 spaces.

}
}
_ = \S.Q.Type.i // okay

_ = \S.Type.Q // expected-error {{key path cannot refer to type 'Q'}}
}