Skip to content

Commit 24bbce1

Browse files
authored
Merge pull request swiftlang#30701 from LucianoPAlmeida/diagnose-key-path-refer-init
[Diagnostics] Diagnose key path referring to init method
2 parents 495b571 + fab4b87 commit 24bbce1

File tree

5 files changed

+21
-8
lines changed

5 files changed

+21
-8
lines changed

lib/Sema/CSDiagnostics.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,24 +1519,26 @@ class InvalidMemberWithMutatingGetterInKeyPath final
15191519
bool diagnoseAsError() override;
15201520
};
15211521

1522-
/// Diagnose an attempt to reference a method as a key path component
1522+
/// Diagnose an attempt to reference a method or initializer as a key path component
15231523
/// e.g.
15241524
///
15251525
/// ```swift
15261526
/// struct S {
1527+
/// init() { }
15271528
/// func foo() -> Int { return 42 }
15281529
/// static func bar() -> Int { return 0 }
15291530
/// }
15301531
///
15311532
/// _ = \S.foo
15321533
/// _ = \S.Type.bar
1534+
/// _ = \S.init
15331535
/// ```
15341536
class InvalidMethodRefInKeyPath final : public InvalidMemberRefInKeyPath {
15351537
public:
15361538
InvalidMethodRefInKeyPath(const Solution &solution, ValueDecl *method,
15371539
ConstraintLocator *locator)
15381540
: InvalidMemberRefInKeyPath(solution, method, locator) {
1539-
assert(isa<FuncDecl>(method));
1541+
assert(isa<FuncDecl>(method) || isa<ConstructorDecl>(method));
15401542
}
15411543

15421544
bool diagnoseAsError() override;

lib/Sema/CSFix.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -747,7 +747,8 @@ bool AllowInvalidRefInKeyPath::diagnose(const Solution &solution,
747747
return failure.diagnose(asNote);
748748
}
749749

750-
case RefKind::Method: {
750+
case RefKind::Method:
751+
case RefKind::Initializer: {
751752
InvalidMethodRefInKeyPath failure(solution, Member, getLocator());
752753
return failure.diagnose(asNote);
753754
}
@@ -764,6 +765,11 @@ AllowInvalidRefInKeyPath::forRef(ConstraintSystem &cs, ValueDecl *member,
764765
return AllowInvalidRefInKeyPath::create(cs, RefKind::Method, member,
765766
locator);
766767

768+
// Referencing initializers in key path is not currently allowed.
769+
if (isa<ConstructorDecl>(member))
770+
return AllowInvalidRefInKeyPath::create(cs, RefKind::Initializer,
771+
member, locator);
772+
767773
// Referencing static members in key path is not currently allowed.
768774
if (member->isStatic())
769775
return AllowInvalidRefInKeyPath::create(cs, RefKind::StaticMember, member,

lib/Sema/CSFix.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,6 +1278,9 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix {
12781278
// Allow a reference to a method (instance or static) as
12791279
// a key path component.
12801280
Method,
1281+
// Allow a reference to a initializer instance as a key path
1282+
// component.
1283+
Initializer,
12811284
} Kind;
12821285

12831286
ValueDecl *Member;
@@ -1297,6 +1300,8 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix {
12971300
"path component";
12981301
case RefKind::Method:
12991302
return "allow reference to a method as a key path component";
1303+
case RefKind::Initializer:
1304+
return "allow reference to an init method as a key path component";
13001305
}
13011306
llvm_unreachable("covered switch");
13021307
}

lib/Sema/CSSimplify.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6305,6 +6305,9 @@ static ConstraintFix *validateInitializerRef(ConstraintSystem &cs,
63056305
// which means MetatypeType has to be added after finding a type variable.
63066306
if (locatorEndsWith(baseLocator, ConstraintLocator::MemberRefBase))
63076307
baseType = MetatypeType::get(baseType);
6308+
} else if (auto *keyPathExpr = dyn_cast<KeyPathExpr>(anchor)) {
6309+
// Key path can't refer to initializers e.g. `\Type.init`
6310+
return AllowInvalidRefInKeyPath::forRef(cs, init, locator);
63086311
}
63096312

63106313
if (!baseType)

test/Constraints/keypath.swift

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,8 @@ let some = Some(keyPath: \Demo.here)
4040
func testFunc() {
4141
let _: (S) -> Int = \.i
4242
_ = ([S]()).map(\.i)
43-
44-
// FIXME: A terrible error, but the same as the pre-existing key path
45-
// error in the similar situation: 'let _ = \S.init'.
46-
_ = ([S]()).map(\.init)
47-
// expected-error@-1 {{type of expression is ambiguous without more context}}
43+
_ = \S.init // expected-error {{key path cannot refer to initializer 'init()'}}
44+
_ = ([S]()).map(\.init) // expected-error {{key path cannot refer to initializer 'init()'}}
4845

4946
let kp = \S.i
5047
let _: KeyPath<S, Int> = kp // works, because type defaults to KeyPath nominal

0 commit comments

Comments
 (0)