Skip to content

Commit 6e35711

Browse files
committed
Noncopyable: prevent keypaths from accessing
We don't yet have keypaths working correctly to allow access to noncopyable fields, so we should raise a friendly error in Sema rather than an error-out elsewhere vaguely. resolves rdar://109287447
1 parent 998986c commit 6e35711

File tree

3 files changed

+80
-6
lines changed

3 files changed

+80
-6
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,8 @@ ERROR(expr_keypath_type_of_property,none,
654654
(DeclNameRef, Type))
655655
ERROR(expr_keypath_generic_type,none,
656656
"key path cannot refer to generic type %0", (Identifier))
657+
ERROR(expr_keypath_noncopyable_type,none,
658+
"key path cannot refer to noncopyable type %0", (Type))
657659
ERROR(expr_keypath_not_property,none,
658660
"%select{key path|dynamic key path member lookup}1 cannot refer to %kind0",
659661
(const ValueDecl *, bool))

lib/Sema/MiscDiagnostics.cpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,8 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
168168
if (isa<TypeExpr>(Base))
169169
checkUseOfMetaTypeName(Base);
170170

171-
if (auto *KPE = dyn_cast<KeyPathExpr>(E)) {
172-
// raise an error if this KeyPath contains an effectful member.
173-
checkForEffectfulKeyPath(KPE);
174-
}
171+
if (auto *KPE = dyn_cast<KeyPathExpr>(E))
172+
checkForInvalidKeyPath(KPE);
175173

176174
// Check function calls, looking through implicit conversions on the
177175
// function and inspecting the arguments directly.
@@ -364,11 +362,15 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
364362
}
365363

366364
/// Visit each component of the keypath and emit a diagnostic if they
367-
/// refer to a member that has effects.
368-
void checkForEffectfulKeyPath(KeyPathExpr *keyPath) {
365+
/// refer to a member that meets any of the following:
366+
/// - has effects.
367+
/// - is a noncopyable type.
368+
void checkForInvalidKeyPath(KeyPathExpr *keyPath) {
369369
for (const auto &component : keyPath->getComponents()) {
370370
if (component.hasDeclRef()) {
371371
auto decl = component.getDeclRef().getDecl();
372+
373+
// Check for effects
372374
if (auto asd = dyn_cast<AbstractStorageDecl>(decl)) {
373375
if (auto getter = asd->getEffectfulGetAccessor()) {
374376
Ctx.Diags.diagnose(component.getLoc(),
@@ -378,6 +380,13 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
378380
asd->getDescriptiveKind());
379381
}
380382
}
383+
384+
// Check for the ability to copy.
385+
if (component.getComponentType()->isNoncopyable()) {
386+
Ctx.Diags.diagnose(component.getLoc(),
387+
diag::expr_keypath_noncopyable_type,
388+
component.getComponentType()->getRValueType());
389+
}
381390
}
382391
}
383392
}

test/Sema/keypaths_noncopyable.swift

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
public protocol P: ~Copyable {
4+
var protocolProp: String { get }
5+
}
6+
7+
public struct NC: ~Copyable {
8+
var data: [Int] = []
9+
var next: Box<NC>? = nil
10+
}
11+
12+
public struct M<T: ~Copyable & P>: ~Copyable {
13+
var nc: NC
14+
var string: String
15+
let ncg: T
16+
init(_ t: consuming T) {
17+
self.string = ""
18+
self.nc = NC()
19+
self.ncg = t
20+
}
21+
}
22+
23+
public class Box<Wrapped: ~Copyable> {
24+
var wrapped: Wrapped
25+
init(_ t: consuming Wrapped) { self.wrapped = t }
26+
func with<T: ~Copyable>(_ op: (borrowing Wrapped)->T) -> T { op(wrapped) }
27+
}
28+
29+
class A {
30+
var b: B
31+
init(_ b: consuming B) { self.b = b }
32+
}
33+
struct B: ~Copyable {
34+
var c: C
35+
}
36+
struct C {
37+
var d: Int
38+
}
39+
40+
// rdar://109287447
41+
public func testKeypath<V: ~Copyable>(m: consuming M<V>) {
42+
_ = m[keyPath: \.nc] // expected-error {{key path cannot refer to noncopyable type 'NC'}}
43+
_ = m[keyPath: \.nc.data] // expected-error {{key path cannot refer to noncopyable type 'NC'}}
44+
_ = m[keyPath: \.ncg] // expected-error {{key path cannot refer to noncopyable type 'V'}}
45+
_ = m[keyPath: \.ncg.protocolProp] // expected-error {{key path cannot refer to noncopyable type 'V'}}
46+
_ = m[keyPath: \.string]
47+
48+
let b = Box(NC())
49+
_ = b.with(\.data)
50+
_ = b.with(\.next)
51+
_ = b.with(\.next?.wrapped) // expected-error {{key path cannot refer to noncopyable type 'NC'}}
52+
_ = b.with(\.next!.wrapped.data) // expected-error {{key path cannot refer to noncopyable type 'NC'}}
53+
}
54+
55+
// rdar://109162739
56+
func testKeypath2(_ someA: A) -> Int {
57+
let kp: KeyPath<A, Int> = \A.b.c.d // expected-error {{key path cannot refer to noncopyable type 'B'}}
58+
return someA[keyPath: kp]
59+
}
60+
func testAsFunc(_ someA: A) -> Int {
61+
let fn: (A) -> Int = \A.b.c.d // expected-error {{key path cannot refer to noncopyable type 'B'}}
62+
return fn(someA)
63+
}

0 commit comments

Comments
 (0)