diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index c193b8727e171..82daf5109924d 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -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", diff --git a/include/swift/Sema/CSFix.h b/include/swift/Sema/CSFix.h index cc53faa9ab1f2..53a3cd70efac4 100644 --- a/include/swift/Sema/CSFix.h +++ b/include/swift/Sema/CSFix.h @@ -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; @@ -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"); } diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 8b376a29bbccc..c34194138d58f 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -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(); diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 1c58ae5862b43..87dcdb89d3f16 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -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(member)); + } + + bool diagnoseAsError() override; +}; + /// Diagnose an attempt to reference a member which has a mutating getter as a /// key path component e.g. /// diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 91f7254a03f51..e10ae20f78fac 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -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"); } @@ -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(member)) + return AllowInvalidRefInKeyPath::create(cs, baseType, RefKind::TypeReference, + member, locator); + return nullptr; } diff --git a/test/expr/unary/keypath/keypath.swift b/test/expr/unary/keypath/keypath.swift index e58cbe6f79a37..7b2d41fb8ec74 100644 --- a/test/expr/unary/keypath/keypath.swift +++ b/test/expr/unary/keypath/keypath.swift @@ -1429,3 +1429,14 @@ func testKeyPathInout() { takesInoutOpt(\.count) takesInoutOpt(\String.count) } + +func testKeypathWithTypeReference() { + struct S { + enum Q { + static let i = 1 + } + } + _ = \S.Q.Type.i // okay + + _ = \S.Type.Q // expected-error {{key path cannot refer to type 'Q'}} +} \ No newline at end of file