Skip to content

Commit 8befa76

Browse files
committed
[Sema] TypeWrapper: verify that type wrapper has both read-only and writable subscripts
1 parent 445148c commit 8befa76

File tree

4 files changed

+97
-0
lines changed

4 files changed

+97
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6571,6 +6571,17 @@ ERROR(type_wrapper_requires_subscript,none,
65716571
" - subscript(storedKeyPath:)",
65726572
(DeclName))
65736573

6574+
ERROR(type_wrapper_requires_readonly_subscript,none,
6575+
"type wrapper type %0 does not contain a required ready-only subscript",
6576+
(DeclName))
6577+
6578+
ERROR(type_wrapper_requires_writable_subscript,none,
6579+
"type wrapper type %0 does not contain a required writable subscript",
6580+
(DeclName))
6581+
6582+
NOTE(add_type_wrapper_subscript_stub_note,none,
6583+
"do you want to add a stub?", ())
6584+
65746585
ERROR(type_wrapper_failable_init,none,
65756586
"type wrapper initializer %0 cannot be failable", (DeclName))
65766587

lib/Sema/TypeCheckAttr.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3771,6 +3771,9 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
37713771
llvm::SmallDenseMap<SubscriptDecl *, SmallVector<UnviabilityReason, 2>, 2>
37723772
nonViableSubscripts;
37733773

3774+
bool hasReadOnly = false;
3775+
bool hasWritable = false;
3776+
37743777
for (auto *decl : subscripts) {
37753778
auto *subscript = cast<SubscriptDecl>(decl);
37763779

@@ -3782,6 +3785,10 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
37823785
BGT->isReferenceWritableKeyPath())) {
37833786
nonViableSubscripts[subscript].push_back(
37843787
UnviabilityReason::InvalidType);
3788+
} else {
3789+
hasReadOnly |= BGT->isKeyPath();
3790+
hasWritable |=
3791+
BGT->isWritableKeyPath() || BGT->isReferenceWritableKeyPath();
37853792
}
37863793
} else {
37873794
nonViableSubscripts[subscript].push_back(
@@ -3793,6 +3800,41 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
37933800
UnviabilityReason::Inaccessible);
37943801
}
37953802

3803+
if (!hasReadOnly) {
3804+
auto &DE = ctx.Diags;
3805+
DE.diagnoseWithNotes(
3806+
DE.diagnose(nominal->getLoc(),
3807+
diag::type_wrapper_requires_readonly_subscript,
3808+
nominal->getName()),
3809+
[&]() {
3810+
DE.diagnose(nominal->getLoc(),
3811+
diag::add_type_wrapper_subscript_stub_note)
3812+
.fixItInsertAfter(
3813+
nominal->getBraces().Start,
3814+
"\nsubscript<Value>(storageKeyPath path: KeyPath<<#Base#>, "
3815+
"Value>) -> Value { get { <#code#> } }");
3816+
});
3817+
attr->setInvalid();
3818+
}
3819+
3820+
if (!hasWritable) {
3821+
auto &DE = ctx.Diags;
3822+
DE.diagnoseWithNotes(
3823+
DE.diagnose(nominal->getLoc(),
3824+
diag::type_wrapper_requires_writable_subscript,
3825+
nominal->getName()),
3826+
[&]() {
3827+
DE.diagnose(nominal->getLoc(),
3828+
diag::add_type_wrapper_subscript_stub_note)
3829+
.fixItInsertAfter(
3830+
nominal->getBraces().Start,
3831+
"\nsubscript<Value>(storageKeyPath path: "
3832+
"WritableKeyPath<<#Base#>, "
3833+
"Value>) -> Value { get { <#code#> } set { <#code#> } }");
3834+
});
3835+
attr->setInvalid();
3836+
}
3837+
37963838
if (subscripts.size() - nonViableSubscripts.size() == 0) {
37973839
for (const auto &entry : nonViableSubscripts) {
37983840
auto *subscript = entry.first;

test/SILOptimizer/type_wrapper_in_user_defined_init_lowering.sil

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import SwiftShims
99
@typeWrapper class MyWrapper<S> {
1010
@_hasStorage var underlying: S { get set }
1111
init(memberwise s: S)
12+
subscript<V>(storageKeyPath path: KeyPath<S, V>) -> V { get }
1213
subscript<V>(storageKeyPath path: WritableKeyPath<S, V>) -> V { get set }
1314
deinit
1415
}

test/type/type_wrapper.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ struct FailableInit<S> {
3333
// Okay because there is a valid `init(memberwise:)` overload.
3434
@typeWrapper
3535
struct FailableAndValidInit<S> {
36+
// expected-error@-1 {{type wrapper type 'FailableAndValidInit' does not contain a required writable subscript}}
37+
// expected-note@-2 {{do you want to add a stub?}} {{33-33=\nsubscript<Value>(storageKeyPath path: WritableKeyPath<<#Base#>, Value>) -> Value { get { <#code#> \} set { <#code#> \} \}}}
38+
3639
init(memberwise: S) {
3740
}
3841

@@ -80,6 +83,11 @@ struct InaccessibleOrInvalidSubscripts<S> {
8083
get { fatalError() }
8184
}
8285

86+
private subscript<V>(storageKeyPath path: WritableKeyPath<S, V>) -> [V] {
87+
// expected-error@-1 {{private subscript 'subscript(storageKeyPath:)' cannot have more restrictive access than its enclosing type wrapper type 'InaccessibleOrInvalidSubscripts' (which is internal)}}
88+
get { fatalError() }
89+
}
90+
8391
subscript(storageKeyPath path: Int) -> Bool { // expected-error {{type wrapper subscript expects a key path parameter type (got: 'Int')}}
8492
get { true }
8593
}
@@ -91,6 +99,10 @@ struct NoopWrapper<S> {
9199

92100
subscript<V>(storageKeyPath path: KeyPath<S, V>) -> V {
93101
get { fatalError() }
102+
}
103+
104+
subscript<V>(storageKeyPath path: WritableKeyPath<S, V>) -> V {
105+
get { fatalError() }
94106
set { }
95107
}
96108
}
@@ -130,6 +142,10 @@ struct Parent {
130142

131143
subscript<V>(storageKeyPath path: KeyPath<S, V>) -> V {
132144
get { fatalError() }
145+
}
146+
147+
subscript<V>(storageKeyPath path: WritableKeyPath<S, V>) -> V {
148+
get { fatalError() }
133149
set { }
134150
}
135151
}
@@ -437,3 +453,30 @@ func testIgnoredAttr() {
437453
}
438454
}
439455
}
456+
457+
func testMissingReadOnlyAndWritableSubscriptsAreDiagnosed() {
458+
@typeWrapper
459+
struct MissingReadOnly<S> {
460+
// expected-error@-1 {{type wrapper type 'MissingReadOnly' does not contain a required ready-only subscript}}
461+
// expected-note@-2 {{do you want to add a stub?}} {{30-30=\nsubscript<Value>(storageKeyPath path: KeyPath<<#Base#>, Value>) -> Value { get { <#code#> \} \}}}
462+
463+
init(memberwise: S) {}
464+
465+
subscript<V>(storageKeyPath path: WritableKeyPath<S, V>) -> V {
466+
get { fatalError() }
467+
set { }
468+
}
469+
}
470+
471+
@typeWrapper
472+
struct MissingWritable<S> {
473+
// expected-error@-1 {{type wrapper type 'MissingWritable' does not contain a required writable subscript}}
474+
// expected-note@-2 {{do you want to add a stub?}} {{30-30=\nsubscript<Value>(storageKeyPath path: WritableKeyPath<<#Base#>, Value>) -> Value { get { <#code#> \} set { <#code#> \} \}}}
475+
476+
init(memberwise: S) {}
477+
478+
subscript<V>(storageKeyPath path: KeyPath<S, V>) -> V {
479+
get { fatalError() }
480+
}
481+
}
482+
}

0 commit comments

Comments
 (0)