1
+ // ===--- RewriteContext.cpp - Term rewriting allocation arena -------------===//
2
+ //
3
+ // This source file is part of the Swift.org open source project
4
+ //
5
+ // Copyright (c) 2021 Apple Inc. and the Swift project authors
6
+ // Licensed under Apache License v2.0 with Runtime Library Exception
7
+ //
8
+ // See https://swift.org/LICENSE.txt for license information
9
+ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10
+ //
11
+ // ===----------------------------------------------------------------------===//
12
+
13
+ #include " swift/AST/Decl.h"
14
+ #include " swift/AST/Types.h"
15
+ #include " ProtocolGraph.h"
16
+ #include " RewriteSystem.h"
17
+ #include " RewriteContext.h"
18
+
19
+ using namespace swift ;
20
+ using namespace rewriting ;
21
+
22
+ Term RewriteContext::getTermForType (CanType paramType,
23
+ const ProtocolDecl *proto) {
24
+ return Term::get (getMutableTermForType (paramType, proto), *this );
25
+ }
26
+
27
+ // / Map an interface type to a term.
28
+ // /
29
+ // / If \p proto is null, this is a term relative to a generic
30
+ // / parameter in a top-level signature. The term is rooted in a generic
31
+ // / parameter atom.
32
+ // /
33
+ // / If \p proto is non-null, this is a term relative to a protocol's
34
+ // / 'Self' type. The term is rooted in a protocol atom for this protocol,
35
+ // / or an associated type atom for some associated type in this protocol.
36
+ // /
37
+ // / Resolved DependentMemberTypes map to associated type atoms.
38
+ // / Unresolved DependentMemberTypes map to name atoms.
39
+ // /
40
+ // / Note the behavior of the root term is special if it is an associated
41
+ // / type atom. The protocol of the associated type is always mapped to
42
+ // / \p proto if it was provided. This ensures we get the correct behavior
43
+ // / if a protocol places a constraint on an associated type inherited from
44
+ // / another protocol:
45
+ // /
46
+ // / protocol P {
47
+ // / associatedtype Foo
48
+ // / }
49
+ // /
50
+ // / protocol Q : P where Foo : R {}
51
+ // /
52
+ // / protocol R {}
53
+ // /
54
+ // / The DependentMemberType in the requirement signature of Q refers to
55
+ // / P::Foo.
56
+ // /
57
+ // / However, we want Q's requirement signature to introduce the rewrite rule
58
+ // /
59
+ // / [Q:Foo].[R] => [Q:Foo]
60
+ // /
61
+ // / and not
62
+ // /
63
+ // / [P:Foo].[R] => [P:Foo]
64
+ // /
65
+ // / This is because the rule only applies to Q's logical override of Foo, and
66
+ // / not P's Foo.
67
+ // /
68
+ // / To handle this, getMutableTermForType() behaves as follows:
69
+ // /
70
+ // / Self.P::Foo with proto = P => [P:Foo]
71
+ // / Self.P::Foo with proto = Q => [Q:Foo]
72
+ // / τ_0_0.P::Foo with proto == nullptr => τ_0_0.[P:Foo]
73
+ // /
74
+ MutableTerm RewriteContext::getMutableTermForType (CanType paramType,
75
+ const ProtocolDecl *proto) {
76
+ assert (paramType->isTypeParameter ());
77
+
78
+ // Collect zero or more nested type names in reverse order.
79
+ bool innermostAssocTypeWasResolved = false ;
80
+
81
+ SmallVector<Atom, 3 > atoms;
82
+ while (auto memberType = dyn_cast<DependentMemberType>(paramType)) {
83
+ paramType = memberType.getBase ();
84
+
85
+ if (auto *assocType = memberType->getAssocType ()) {
86
+ const auto *thisProto = assocType->getProtocol ();
87
+ if (proto && isa<GenericTypeParamType>(paramType)) {
88
+ thisProto = proto;
89
+ innermostAssocTypeWasResolved = true ;
90
+ }
91
+ atoms.push_back (Atom::forAssociatedType (thisProto,
92
+ assocType->getName (),
93
+ *this ));
94
+ } else {
95
+ atoms.push_back (Atom::forName (memberType->getName (), *this ));
96
+ innermostAssocTypeWasResolved = false ;
97
+ }
98
+ }
99
+
100
+ // Add the root atom at the end.
101
+ if (proto) {
102
+ assert (proto->getSelfInterfaceType ()->isEqual (paramType));
103
+
104
+ // Self.Foo becomes [P].Foo
105
+ // Self.Q::Foo becomes [P:Foo] (not [Q:Foo] or [P].[Q:Foo])
106
+ if (!innermostAssocTypeWasResolved)
107
+ atoms.push_back (Atom::forProtocol (proto, *this ));
108
+ } else {
109
+ atoms.push_back (Atom::forGenericParam (
110
+ cast<GenericTypeParamType>(paramType), *this ));
111
+ }
112
+
113
+ std::reverse (atoms.begin (), atoms.end ());
114
+
115
+ return MutableTerm (atoms);
116
+ }
117
+
118
+ // / Compute the interface type for a range of atoms, with an optional
119
+ // / root type.
120
+ // /
121
+ // / If the root type is specified, we wrap it in a series of
122
+ // / DependentMemberTypes. Otherwise, the root is computed from
123
+ // / the first atom of the range.
124
+ template <typename Iter>
125
+ Type getTypeForAtomRange (Iter begin, Iter end, Type root,
126
+ TypeArrayView<GenericTypeParamType> genericParams,
127
+ const ProtocolGraph &protos,
128
+ ASTContext &ctx) {
129
+ Type result = root;
130
+
131
+ auto handleRoot = [&](GenericTypeParamType *genericParam) {
132
+ assert (genericParam->isCanonical ());
133
+
134
+ if (!genericParams.empty ()) {
135
+ // Return a sugared GenericTypeParamType if we're given an array of
136
+ // sugared types to substitute.
137
+ unsigned index = GenericParamKey (genericParam).findIndexIn (genericParams);
138
+ result = genericParams[index];
139
+ return ;
140
+ }
141
+
142
+ // Otherwise, we're going to return a canonical type.
143
+ result = genericParam;
144
+ };
145
+
146
+ for (; begin != end; ++begin) {
147
+ auto atom = *begin;
148
+
149
+ if (!result) {
150
+ // A valid term always begins with a generic parameter, protocol or
151
+ // associated type atom.
152
+ switch (atom.getKind ()) {
153
+ case Atom::Kind::GenericParam:
154
+ handleRoot (atom.getGenericParam ());
155
+ continue ;
156
+
157
+ case Atom::Kind::Protocol:
158
+ handleRoot (GenericTypeParamType::get (0 , 0 , ctx));
159
+ continue ;
160
+
161
+ case Atom::Kind::AssociatedType:
162
+ handleRoot (GenericTypeParamType::get (0 , 0 , ctx));
163
+
164
+ // An associated type term at the root means we have a dependent
165
+ // member type rooted at Self; handle the associated type below.
166
+ break ;
167
+
168
+ case Atom::Kind::Name:
169
+ case Atom::Kind::Layout:
170
+ case Atom::Kind::Superclass:
171
+ case Atom::Kind::ConcreteType:
172
+ llvm_unreachable (" Term has invalid root atom" );
173
+ }
174
+ }
175
+
176
+ // An unresolved type can appear if we have invalid requirements.
177
+ if (atom.getKind () == Atom::Kind::Name) {
178
+ result = DependentMemberType::get (result, atom.getName ());
179
+ continue ;
180
+ }
181
+
182
+ // We should have a resolved type at this point.
183
+ assert (atom.getKind () == Atom::Kind::AssociatedType);
184
+ auto *proto = atom.getProtocols ()[0 ];
185
+ auto name = atom.getName ();
186
+
187
+ AssociatedTypeDecl *assocType = nullptr ;
188
+
189
+ // Special case: handle unknown protocols, since they can appear in the
190
+ // invalid types that getCanonicalTypeInContext() must handle via
191
+ // concrete substitution; see the definition of getCanonicalTypeInContext()
192
+ // below for details.
193
+ if (!protos.isKnownProtocol (proto)) {
194
+ assert (root &&
195
+ " We only allow unknown protocols in getRelativeTypeForTerm()" );
196
+ assert (atom.getProtocols ().size () == 1 &&
197
+ " Unknown associated type atom must have a single protocol" );
198
+ assocType = proto->getAssociatedType (name)->getAssociatedTypeAnchor ();
199
+ } else {
200
+ // FIXME: Cache this
201
+ //
202
+ // An associated type atom [P1&P1&...&Pn:A] has one or more protocols
203
+ // P0...Pn and an identifier 'A'.
204
+ //
205
+ // We map it back to a AssociatedTypeDecl as follows:
206
+ //
207
+ // - For each protocol Pn, look for associated types A in Pn itself,
208
+ // and all protocols that Pn refines.
209
+ //
210
+ // - For each candidate associated type An in protocol Qn where
211
+ // Pn refines Qn, get the associated type anchor An' defined in
212
+ // protocol Qn', where Qn refines Qn'.
213
+ //
214
+ // - Out of all the candidiate pairs (Qn', An'), pick the one where
215
+ // the protocol Qn' is the lowest element according to the linear
216
+ // order defined by TypeDecl::compare().
217
+ //
218
+ // The associated type An' is then the canonical associated type
219
+ // representative of the associated type atom [P0&...&Pn:A].
220
+ //
221
+ for (auto *proto : atom.getProtocols ()) {
222
+ const auto &info = protos.getProtocolInfo (proto);
223
+ auto checkOtherAssocType = [&](AssociatedTypeDecl *otherAssocType) {
224
+ otherAssocType = otherAssocType->getAssociatedTypeAnchor ();
225
+
226
+ if (otherAssocType->getName () == name &&
227
+ (assocType == nullptr ||
228
+ TypeDecl::compare (otherAssocType->getProtocol (),
229
+ assocType->getProtocol ()) < 0 )) {
230
+ assocType = otherAssocType;
231
+ }
232
+ };
233
+
234
+ for (auto *otherAssocType : info.AssociatedTypes ) {
235
+ checkOtherAssocType (otherAssocType);
236
+ }
237
+
238
+ for (auto *otherAssocType : info.InheritedAssociatedTypes ) {
239
+ checkOtherAssocType (otherAssocType);
240
+ }
241
+ }
242
+ }
243
+
244
+ assert (assocType && " Need to look harder" );
245
+ result = DependentMemberType::get (result, assocType);
246
+ }
247
+
248
+ return result;
249
+ }
250
+
251
+ Type RewriteContext::getTypeForTerm (Term term,
252
+ TypeArrayView<GenericTypeParamType> genericParams,
253
+ const ProtocolGraph &protos) const {
254
+ return getTypeForAtomRange (term.begin (), term.end (), Type (),
255
+ genericParams, protos, Context);
256
+ }
257
+
258
+ Type RewriteContext::getTypeForTerm (const MutableTerm &term,
259
+ TypeArrayView<GenericTypeParamType> genericParams,
260
+ const ProtocolGraph &protos) const {
261
+ return getTypeForAtomRange (term.begin (), term.end (), Type (),
262
+ genericParams, protos, Context);
263
+ }
264
+
265
+ Type RewriteContext::getRelativeTypeForTerm (
266
+ const MutableTerm &term, const MutableTerm &prefix,
267
+ const ProtocolGraph &protos) const {
268
+ assert (std::equal (prefix.begin (), prefix.end (), term.begin ()));
269
+
270
+ auto genericParam = CanGenericTypeParamType::get (0 , 0 , Context);
271
+ return getTypeForAtomRange (
272
+ term.begin () + prefix.size (), term.end (), genericParam,
273
+ { }, protos, Context);
274
+ }
0 commit comments