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