Skip to content

Commit c003e69

Browse files
committed
[Sema] TypeWrappers: Allow type wrapper inference from protocol conformances
- Infer type wrappers only from direct (declared on type) protocols - Inferences from protocol to protocol is not allowed - If type specifies a type wrapper attribute explicitly it has to match the one associated with a declared protocol, otherwise the declaration is going to be rejected as having multiple wrappers.
1 parent e5151b3 commit c003e69

File tree

6 files changed

+127
-14
lines changed

6 files changed

+127
-14
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6616,6 +6616,10 @@ ERROR(cannot_use_multiple_type_wrappers,none,
66166616
"%0 %1 cannot use more than one type wrapper",
66176617
(DescriptiveDeclKind, DeclName))
66186618

6619+
NOTE(type_wrapper_inferred_from,none,
6620+
"type wrapper %0 inferred from %1 %2",
6621+
(DeclName, DescriptiveDeclKind, DeclName))
6622+
66196623
ERROR(type_wrapper_requires_memberwise_init,none,
66206624
"type wrapper type %0 does not contain a required initializer"
66216625
" - init(for:storage:)",

lib/Sema/TypeCheckTypeWrapper.cpp

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,18 @@ NominalTypeDecl *NominalTypeDecl::getTypeWrapper() const {
8383
GetTypeWrapper{mutableSelf}, nullptr);
8484
}
8585

86-
static void getTypeWrappers(
87-
NominalTypeDecl *decl,
88-
SmallVectorImpl<std::pair<CustomAttr *, NominalTypeDecl *>> &typeWrappers) {
86+
struct TypeWrapperAttrInfo {
87+
CustomAttr *Attr;
88+
NominalTypeDecl *Wrapper;
89+
NominalTypeDecl *AttachedTo;
90+
};
91+
92+
static void
93+
getTypeWrappers(NominalTypeDecl *decl,
94+
SmallVectorImpl<TypeWrapperAttrInfo> &typeWrappers) {
8995
auto &ctx = decl->getASTContext();
9096

97+
// Attributes applied directly to the type.
9198
for (auto *attr : decl->getAttrs().getAttributes<CustomAttr>()) {
9299
auto *mutableAttr = const_cast<CustomAttr *>(attr);
93100
auto *nominal = evaluateOrDefault(
@@ -98,7 +105,39 @@ static void getTypeWrappers(
98105

99106
auto *typeWrapper = nominal->getAttrs().getAttribute<TypeWrapperAttr>();
100107
if (typeWrapper && typeWrapper->isValid())
101-
typeWrappers.push_back({mutableAttr, nominal});
108+
typeWrappers.push_back({mutableAttr, nominal, decl});
109+
}
110+
111+
// Do not allow transitive protocol inference between protocols.
112+
if (isa<ProtocolDecl>(decl))
113+
return;
114+
115+
// Attributes inferred from (explicit) protocol conformances
116+
// associated with the declaration of the type.
117+
for (unsigned i : indices(decl->getInherited())) {
118+
auto inheritedType = evaluateOrDefault(
119+
ctx.evaluator,
120+
InheritedTypeRequest{decl, i, TypeResolutionStage::Interface}, Type());
121+
122+
if (!(inheritedType && inheritedType->isConstraintType()))
123+
continue;
124+
125+
auto *protocol = inheritedType->getAnyNominal();
126+
if (!protocol)
127+
continue;
128+
129+
SmallVector<TypeWrapperAttrInfo, 2> inferredAttrs;
130+
getTypeWrappers(protocol, inferredAttrs);
131+
132+
// De-duplicate inferred type wrappers. This also makes sure
133+
// that if both protocol and conforming type explicitly declare
134+
// the same type wrapper there is no clash between them.
135+
for (const auto &inferredAttr : inferredAttrs) {
136+
if (llvm::find_if(typeWrappers, [&](const TypeWrapperAttrInfo &attr) {
137+
return attr.Wrapper == inferredAttr.Wrapper;
138+
}) == typeWrappers.end())
139+
typeWrappers.push_back(inferredAttr);
140+
}
102141
}
103142
}
104143

@@ -108,7 +147,7 @@ NominalTypeDecl *GetTypeWrapper::evaluate(Evaluator &evaluator,
108147

109148
// Note that we don't actually care whether there are duplicates,
110149
// using the same type wrapper multiple times is still an error.
111-
SmallVector<std::pair<CustomAttr *, NominalTypeDecl *>, 2> typeWrappers;
150+
SmallVector<TypeWrapperAttrInfo, 2> typeWrappers;
112151

113152
getTypeWrappers(decl, typeWrappers);
114153

@@ -119,27 +158,34 @@ NominalTypeDecl *GetTypeWrapper::evaluate(Evaluator &evaluator,
119158
ctx.Diags.diagnose(decl, diag::cannot_use_multiple_type_wrappers,
120159
decl->getDescriptiveKind(), decl->getName());
121160

122-
for (const auto &attr : typeWrappers) {
123-
ctx.Diags.diagnose(attr.first->getLocation(), diag::decl_declared_here,
124-
attr.second->getName());
161+
for (const auto &entry : typeWrappers) {
162+
if (entry.AttachedTo == decl) {
163+
ctx.Diags.diagnose(entry.Attr->getLocation(), diag::decl_declared_here,
164+
entry.Wrapper->getName());
165+
} else {
166+
ctx.Diags.diagnose(decl, diag::type_wrapper_inferred_from,
167+
entry.Wrapper->getName(),
168+
entry.AttachedTo->getDescriptiveKind(),
169+
entry.AttachedTo->getName());
170+
}
125171
}
126172

127173
return nullptr;
128174
}
129175

130-
return typeWrappers.front().second;
176+
return typeWrappers.front().Wrapper;
131177
}
132178

133179
Type GetTypeWrapperType::evaluate(Evaluator &evaluator,
134180
NominalTypeDecl *decl) const {
135-
SmallVector<std::pair<CustomAttr *, NominalTypeDecl *>, 2> typeWrappers;
181+
SmallVector<TypeWrapperAttrInfo, 2> typeWrappers;
136182

137183
getTypeWrappers(decl, typeWrappers);
138184

139185
if (typeWrappers.size() != 1)
140186
return Type();
141187

142-
auto *typeWrapperAttr = typeWrappers.front().first;
188+
auto *typeWrapperAttr = typeWrappers.front().Attr;
143189
auto type = evaluateOrDefault(
144190
evaluator,
145191
CustomAttrTypeRequest{typeWrapperAttr, decl->getDeclContext(),

test/Interpreter/Inputs/type_wrapper_defs.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,3 +394,10 @@ public class TypeWithSomeDefaultedLetProperties<T> {
394394
print(self.c)
395395
}
396396
}
397+
398+
@Wrapper
399+
public protocol WrappedProtocol {
400+
associatedtype T : RangeReplaceableCollection
401+
402+
var v: T { get set }
403+
}

test/Interpreter/type_wrappers.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,3 +666,26 @@ do {
666666
// CHECK-NEXT: in getter storage: \$Storage.b
667667
// CHECK-NEXT: 42
668668
}
669+
670+
// Type wrapper inference from protocols
671+
do {
672+
struct Test<T> : WrappedProtocol {
673+
var v: [T?]
674+
675+
init(_ value: T? = nil) {
676+
v = [value]
677+
}
678+
}
679+
680+
func run_test<T: WrappedProtocol>(_ test: T) {
681+
print(test.v)
682+
print(test.v.count)
683+
}
684+
685+
run_test(Test(Optional("Hello")))
686+
// CHECK: Wrapper.init(for: Test<String>, storage: $Storage(v: [Optional("Hello")]))
687+
// CHECK-NEXT: in getter storage: \$Storage.v
688+
// CHECK-NEXT: [Optional("Hello")]
689+
// CHECK-NEXT: in getter storage: \$Storage.v
690+
// CHECK-NEXT: 1
691+
}

test/Serialization/AllowErrors/invalid-inheritance.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,8 @@ extension undefined: SomeProto {} // expected-error {{cannot find type 'undefine
3232

3333
public extension undefined { // expected-error {{cannot find type 'undefined' in scope}}
3434
protocol SomeProtoInner: undefined {} // expected-error {{cannot find type 'undefined' in scope}}
35-
// TODO: Why don't these have errors?
36-
class SomeClassInner: undefined {}
37-
struct SomeStructInner: undefined {}
35+
class SomeClassInner: undefined {} // expected-error {{cannot find type 'undefined' in scope}}
36+
struct SomeStructInner: undefined {} // expected-error {{cannot find type 'undefined' in scope}}
3837
enum SomeEnumInner: undefined { // expected-error {{cannot find type 'undefined' in scope}}
3938
case a
4039
}

test/type/type_wrapper.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,3 +653,37 @@ func test_multiple_wrapper_attributes() {
653653
// expected-note@-2 {{'NoopWrapper' declared here}}
654654
class Test2 {} // expected-error {{class 'Test2' cannot use more than one type wrapper}}
655655
}
656+
657+
@NoopWrapper
658+
protocol WrappedProto {
659+
}
660+
661+
do {
662+
// @NoopWrapper is inferred from `WrappedProto`
663+
struct InferenceFromProto : WrappedProto { // Ok
664+
var x: Int
665+
666+
func test() {
667+
_ = $storage // Ok (to make sure that NoopWrapper is indeed inferred)
668+
}
669+
}
670+
671+
@Parent.Wrapper // expected-note {{'Wrapper' declared here}}
672+
struct ClashBetweenDirectAndInferred : WrappedProto {
673+
// expected-error@-1 {{struct 'ClashBetweenDirectAndInferred' cannot use more than one type wrapper}}
674+
// expected-note@-2 {{type wrapper 'NoopWrapper' inferred from protocol 'WrappedProto'}}
675+
// expected-error@-3 {{type 'ClashBetweenDirectAndInferred' does not conform to protocol 'WrappedProto'}}
676+
}
677+
678+
@NoopWrapper
679+
final class NoClashDueToSameWrapper : WrappedProto { // Ok
680+
var v: [Int?]
681+
682+
func test() {
683+
let storage: NoopWrapper<NoClashDueToSameWrapper, $Storage> = $storage
684+
_ = storage[propertyKeyPath: \.v, storageKeyPath: \$Storage.v]
685+
}
686+
}
687+
688+
689+
}

0 commit comments

Comments
 (0)