Skip to content

Commit 5f2cd38

Browse files
committed
[Sema] TypeWrappers: Introduce a Wrapped type parameter requirement
This parameter should be the first in the list of generic parameters. `for: Wrapped.Type` parameter is added to a required initializer.
1 parent 70b0cd5 commit 5f2cd38

File tree

3 files changed

+157
-44
lines changed

3 files changed

+157
-44
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6591,18 +6591,27 @@ ERROR(type_wrapper_attribute_not_allowed_here,none,
65916591
"type wrapper attribute %0 can only be applied to a class, struct",
65926592
(Identifier))
65936593

6594-
ERROR(type_wrapper_requires_a_single_generic_param,none,
6595-
"type wrapper has to declare a single generic parameter "
6596-
"for underlying storage type", ())
6594+
ERROR(type_wrapper_requires_two_generic_params,none,
6595+
"type wrapper has to declare two generic parameters: "
6596+
"wrapped and storage types", ())
65976597

65986598
ERROR(cannot_use_multiple_type_wrappers,none,
65996599
"type %0 cannot use more than one type wrapper", ())
66006600

66016601
ERROR(type_wrapper_requires_memberwise_init,none,
66026602
"type wrapper type %0 does not contain a required initializer"
6603-
" - init(storage:)",
6603+
" - init(for:storage:)",
66046604
(DeclName))
66056605

6606+
ERROR(cannot_overload_type_wrapper_initializer,none,
6607+
"cannot overload type wrapper initializer 'init(for:storage:)'", ())
6608+
6609+
ERROR(cannot_declare_type_wrapper_init_with_invalid_first_param,none,
6610+
"first parameter of type wrapper initializer should be wrapped type - %0", (Type))
6611+
6612+
ERROR(cannot_declare_type_wrapper_init_with_invalid_second_param,none,
6613+
"second parameter of type wrapper initializer should be storage type - %0", (Type))
6614+
66066615
ERROR(type_wrapper_requires_subscript,none,
66076616
"type wrapper type %0 does not contain a required subscript"
66086617
" - subscript(storedKeyPath:)",

lib/Sema/TypeCheckAttr.cpp

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3750,25 +3750,25 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
37503750

37513751
// Check whether type marked as @typeWrapper is valid:
37523752
//
3753-
// - Has a single generic parameter <Storage>
3754-
// - Has `init(storage: <Storage>)`
3753+
// - Has two generic parameters - `Wrapped` and `Storage`
3754+
// - Has `init(for: <Wrapped>.Type, storage: <Storage>)`
37553755
// - Has at least one `subscript(storedKeyPath: KeyPath<...>)` overload
37563756

37573757
// Has a single generic parameter.
3758+
auto *genericParams = nominal->getGenericParams();
37583759
{
3759-
auto *genericParams = nominal->getGenericParams();
3760-
if (!genericParams || genericParams->size() != 1) {
3760+
if (!genericParams || genericParams->size() != 2) {
37613761
diagnose(nominal->getLoc(),
3762-
diag::type_wrapper_requires_a_single_generic_param);
3762+
diag::type_wrapper_requires_two_generic_params);
37633763
attr->setInvalid();
37643764
return;
37653765
}
37663766
}
37673767

3768-
// `init(storage:)`
3768+
// `init(for:storage:)`
37693769
{
37703770
DeclName initName(ctx, DeclBaseName::createConstructor(),
3771-
ArrayRef<Identifier>(ctx.Id_storage));
3771+
{ctx.Id_for, ctx.Id_storage});
37723772

37733773
SmallVector<ValueDecl *, 2> inits;
37743774
if (findMembersOrDiagnose(initName, inits,
@@ -3787,8 +3787,11 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
37873787
nonViableInits[init].push_back(UnviabilityReason::Inaccessible);
37883788
}
37893789

3790-
// If there are no viable initializers, let's complain.
3791-
if (inits.size() - nonViableInits.size() == 0) {
3790+
unsigned numViable = inits.size() - nonViableInits.size();
3791+
3792+
switch (numViable) {
3793+
case 0: {
3794+
// If there are no viable initializers, let's complain.
37923795
for (const auto &entry : nonViableInits) {
37933796
auto *init = entry.first;
37943797

@@ -3814,6 +3817,61 @@ void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) {
38143817
attr->setInvalid();
38153818
return;
38163819
}
3820+
3821+
case 1: {
3822+
// If there is only one choice let's check whether it's correct.
3823+
for (auto *decl : inits) {
3824+
auto *ctor = cast<ConstructorDecl>(decl);
3825+
3826+
if (nonViableInits.count(ctor))
3827+
continue;
3828+
3829+
auto wrappedType = ctor->getParameters()->get(0)->getInterfaceType();
3830+
auto storageType = ctor->getParameters()->get(1)->getInterfaceType();
3831+
3832+
auto typeWrapperGenericParams = genericParams->getParams();
3833+
3834+
// Let's check wrapped type - it should be a metatype of the first
3835+
// generic parameter - <Wrapped>.
3836+
{
3837+
auto wrappedTypeParamTy =
3838+
typeWrapperGenericParams[0]->getInterfaceType();
3839+
if (!wrappedType->isEqual(wrappedTypeParamTy)) {
3840+
diagnose(
3841+
ctor,
3842+
diag::cannot_declare_type_wrapper_init_with_invalid_first_param,
3843+
wrappedTypeParamTy);
3844+
ctor->setInvalid();
3845+
}
3846+
}
3847+
3848+
// Second parameter should be <Storage> generic parameter type.
3849+
{
3850+
auto storageTypeParamTy = typeWrapperGenericParams[1]
3851+
->getInterfaceType()
3852+
->getMetatypeInstanceType();
3853+
if (!storageType->isEqual(storageTypeParamTy)) {
3854+
diagnose(
3855+
ctor,
3856+
diag::
3857+
cannot_declare_type_wrapper_init_with_invalid_second_param,
3858+
storageTypeParamTy);
3859+
ctor->setInvalid();
3860+
}
3861+
}
3862+
3863+
if (ctor->isInvalid()) {
3864+
attr->setInvalid();
3865+
return;
3866+
}
3867+
}
3868+
break;
3869+
}
3870+
3871+
default:
3872+
diagnose(inits.front(), diag::cannot_overload_type_wrapper_initializer);
3873+
return;
3874+
}
38173875
}
38183876

38193877
// subscript(storedKeypath: KeyPath<...>)

test/type/type_wrapper.swift

Lines changed: 77 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,43 @@
33
// REQUIRES: asserts
44

55
@typeWrapper
6-
struct ConcreteTypeWrapper { // expected-error {{type wrapper has to declare a single generic parameter for underlying storage type}}
6+
struct ConcreteTypeWrapper { // expected-error {{type wrapper has to declare two generic parameters: wrapped and storage types}}
77
init(storage: Int) {}
88
}
99

1010
@typeWrapper
11-
struct EmptyTypeWrapper<S> { // expected-error {{type wrapper type 'EmptyTypeWrapper' does not contain a required initializer - init(storage:)}}
11+
struct EmptyTypeWrapper<W, S> { // expected-error {{type wrapper type 'EmptyTypeWrapper' does not contain a required initializer - init(for:storage:)}}
1212
}
1313

1414
@typeWrapper
15-
struct NoMemberwiseInit<S> {
16-
// expected-error@-1 {{type wrapper type 'NoMemberwiseInit' does not contain a required initializer - init(storage:)}}
15+
struct NoMemberwiseInit<W, S> {
16+
// expected-error@-1 {{type wrapper type 'NoMemberwiseInit' does not contain a required initializer - init(for:storage:)}}
1717

1818
subscript<V>(storageKeyPath path: KeyPath<S, V>) -> V {
1919
get { fatalError() }
2020
}
2121
}
2222

2323
@typeWrapper
24-
struct FailableInit<S> {
25-
init?(storage: S) { // expected-error {{type wrapper initializer 'init(storage:)' cannot be failable}}
24+
struct FailableInit<W, S> {
25+
init?(for: W.Type, storage: S) { // expected-error {{type wrapper initializer 'init(for:storage:)' cannot be failable}}
2626
}
2727

2828
subscript<V>(storageKeyPath path: KeyPath<S, V>) -> V {
2929
get { fatalError() }
3030
}
3131
}
3232

33-
// Okay because there is a valid `init(storage:)` overload.
33+
// Okay because there is a valid `init(for:storage:)` overload.
3434
@typeWrapper
35-
struct FailableAndValidInit<S> {
35+
struct FailableAndValidInit<W, S> {
3636
// 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#> \} \}}}
37+
// expected-note@-2 {{do you want to add a stub?}} {{36-36=\nsubscript<Value>(storageKeyPath path: WritableKeyPath<<#Base#>, Value>) -> Value { get { <#code#> \} set { <#code#> \} \}}}
3838

39-
init(storage: S) {
39+
init(for: W.Type, storage: S) {
4040
}
4141

42-
init?(storage: S) where S == Int {
42+
init?(for: W.Type, storage: S) where S == Int {
4343
}
4444

4545
subscript<V>(storageKeyPath path: KeyPath<S, V>) -> V {
@@ -48,14 +48,14 @@ struct FailableAndValidInit<S> {
4848
}
4949

5050
@typeWrapper
51-
public struct InaccessibleInit<S> {
52-
fileprivate init(storage: S) {
53-
// expected-error@-1 {{fileprivate initializer 'init(storage:)' cannot have more restrictive access than its enclosing type wrapper type 'InaccessibleInit' (which is public)}}
51+
public struct InaccessibleInit<W, S> {
52+
fileprivate init(for: W.Type, storage: S) {
53+
// expected-error@-1 {{fileprivate initializer 'init(for:storage:)' cannot have more restrictive access than its enclosing type wrapper type 'InaccessibleInit' (which is public)}}
5454
}
5555

56-
private init?(storage: S) where S: AnyObject {
57-
// expected-error@-1 {{private initializer 'init(storage:)' cannot have more restrictive access than its enclosing type wrapper type 'InaccessibleInit' (which is public)}}
58-
// expected-error@-2 {{type wrapper initializer 'init(storage:)' cannot be failable}}
56+
private init?(for: W.Type, storage: S) where S: AnyObject {
57+
// expected-error@-1 {{private initializer 'init(for:storage:)' cannot have more restrictive access than its enclosing type wrapper type 'InaccessibleInit' (which is public)}}
58+
// expected-error@-2 {{type wrapper initializer 'init(for:storage:)' cannot be failable}}
5959
}
6060

6161
subscript<V>(storageKeyPath path: KeyPath<S, V>) -> V {
@@ -64,14 +64,14 @@ public struct InaccessibleInit<S> {
6464
}
6565

6666
@typeWrapper
67-
struct NoSubscripts<S> {
67+
struct NoSubscripts<W, S> {
6868
// expected-error@-1 {{type wrapper type 'NoSubscripts' does not contain a required subscript - subscript(storedKeyPath:)}}
69-
init(storage: S) {}
69+
init(for: W.Type, storage: S) {}
7070
}
7171

7272
@typeWrapper
73-
struct InaccessibleOrInvalidSubscripts<S> {
74-
init(storage: S) {}
73+
struct InaccessibleOrInvalidSubscripts<W, S> {
74+
init(for: W.Type, storage: S) {}
7575

7676
fileprivate subscript<V>(storageKeyPath path: KeyPath<S, V>) -> V {
7777
// expected-error@-1 {{fileprivate subscript 'subscript(storageKeyPath:)' cannot have more restrictive access than its enclosing type wrapper type 'InaccessibleOrInvalidSubscripts' (which is internal)}}
@@ -94,8 +94,54 @@ struct InaccessibleOrInvalidSubscripts<S> {
9494
}
9595

9696
@typeWrapper
97-
struct NoopWrapper<S> {
98-
init(storage: S) {}
97+
struct OverloadedCtorWrapper<W, S> {
98+
init(for: W.Type, storage: S) {} // expected-error {{cannot overload type wrapper initializer 'init(for:storage:)'}}
99+
init(for: W.Type, storage: Int) {}
100+
101+
subscript<V>(storageKeyPath path: KeyPath<S, V>) -> [V] { get { fatalError() } }
102+
subscript<V>(storageKeyPath path: WritableKeyPath<S, V>) -> [V] {
103+
get { fatalError() }
104+
set { }
105+
}
106+
}
107+
108+
do {
109+
@typeWrapper
110+
struct InvalidInit1<W, S> {
111+
init(for: Int, storage: S) {} // expected-error {{first parameter of type wrapper initializer should be wrapped type - 'W.Type'}}
112+
}
113+
114+
@typeWrapper
115+
struct InvalidInit2<W, S> {
116+
init(for: W, storage: S) {} // expected-error {{first parameter of type wrapper initializer should be wrapped type - 'W.Type'}}
117+
}
118+
119+
@typeWrapper
120+
struct InvalidInit3<W, S> {
121+
init(for: Int.Type, storage: S) {} // expected-error {{first parameter of type wrapper initializer should be wrapped type - 'W.Type'}}
122+
}
123+
124+
@typeWrapper
125+
struct InvalidInit4<W, S> {
126+
init(for: W.Type, storage: Int) {} // expected-error {{second parameter of type wrapper initializer should be storage type - 'S'}}
127+
}
128+
129+
@typeWrapper
130+
struct InvalidInit5<W, S> {
131+
init(for: W.Type, storage: UnknownType) {} // expected-error {{second parameter of type wrapper initializer should be storage type - 'S'}}
132+
// expected-error@-1 {{cannot find type 'UnknownType' in scope}}
133+
}
134+
135+
@typeWrapper
136+
struct InvalidInit6<W, S> {
137+
init(for: UnknownType.Type, storage: S) {} // expected-error {{first parameter of type wrapper initializer should be wrapped type - 'W.Type'}}
138+
// expected-error@-1 {{cannot find type 'UnknownType' in scope}}
139+
}
140+
}
141+
142+
@typeWrapper
143+
struct NoopWrapper<W, S> {
144+
init(for: W.Type, storage: S) {}
99145

100146
subscript<V>(storageKeyPath path: KeyPath<S, V>) -> V {
101147
get { fatalError() }
@@ -137,8 +183,8 @@ func testWrappedTypeAccessChecking() {
137183

138184
struct Parent {
139185
@typeWrapper
140-
struct Wrapper<S> {
141-
init(storage: S) {}
186+
struct Wrapper<W, S> {
187+
init(for: W.Type, storage: S) {}
142188

143189
subscript<V>(storageKeyPath path: KeyPath<S, V>) -> V {
144190
get { fatalError() }
@@ -456,11 +502,11 @@ func testIgnoredAttr() {
456502

457503
func testMissingReadOnlyAndWritableSubscriptsAreDiagnosed() {
458504
@typeWrapper
459-
struct MissingReadOnly<S> {
505+
struct MissingReadOnly<W, S> {
460506
// 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#> \} \}}}
507+
// expected-note@-2 {{do you want to add a stub?}} {{33-33=\nsubscript<Value>(storageKeyPath path: KeyPath<<#Base#>, Value>) -> Value { get { <#code#> \} \}}}
462508

463-
init(storage: S) {}
509+
init(for: W.Type, storage: S) {}
464510

465511
subscript<V>(storageKeyPath path: WritableKeyPath<S, V>) -> V {
466512
get { fatalError() }
@@ -469,11 +515,11 @@ func testMissingReadOnlyAndWritableSubscriptsAreDiagnosed() {
469515
}
470516

471517
@typeWrapper
472-
struct MissingWritable<S> {
518+
struct MissingWritable<W, S> {
473519
// 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#> \} \}}}
520+
// expected-note@-2 {{do you want to add a stub?}} {{33-33=\nsubscript<Value>(storageKeyPath path: WritableKeyPath<<#Base#>, Value>) -> Value { get { <#code#> \} set { <#code#> \} \}}}
475521

476-
init(storage: S) {}
522+
init(for: W.Type, storage: S) {}
477523

478524
subscript<V>(storageKeyPath path: KeyPath<S, V>) -> V {
479525
get { fatalError() }

0 commit comments

Comments
 (0)