Skip to content

Commit 4184ebd

Browse files
committed
RequirementMachine: Split off RewriteContext.{h,cpp} from RewriteSystem.{h,cpp}
1 parent d7bb7e6 commit 4184ebd

File tree

7 files changed

+364
-308
lines changed

7 files changed

+364
-308
lines changed

lib/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ add_swift_host_library(swiftAST STATIC
7676
RequirementMachine/EquivalenceClassMap.cpp
7777
RequirementMachine/ProtocolGraph.cpp
7878
RequirementMachine/RequirementMachine.cpp
79+
RequirementMachine/RewriteContext.cpp
7980
RequirementMachine/RewriteSystem.cpp
8081
RequirementMachine/RewriteSystemCompletion.cpp
8182
SILLayout.cpp

lib/AST/RequirementMachine/EquivalenceClassMap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <vector>
2828

2929
#include "ProtocolGraph.h"
30+
#include "RewriteContext.h"
3031
#include "RewriteSystem.h"
3132

3233
namespace llvm {

lib/AST/RequirementMachine/RequirementMachine.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===--- RequirementMachine.cpp - Generics with term rewriting --*- C++ -*-===//
1+
//===--- RequirementMachine.cpp - Generics with term rewriting ------------===//
22
//
33
// This source file is part of the Swift.org open source project
44
//
@@ -23,6 +23,7 @@
2323

2424
#include "EquivalenceClassMap.h"
2525
#include "ProtocolGraph.h"
26+
#include "RewriteContext.h"
2627
#include "RewriteSystem.h"
2728

2829
using namespace swift;
Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
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+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//===--- RewriteContext.h - Term rewriting allocation arena -----*- C++ -*-===//
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+
#ifndef SWIFT_REWRITECONTEXT_H
14+
#define SWIFT_REWRITECONTEXT_H
15+
16+
#include "swift/AST/ASTContext.h"
17+
#include "swift/AST/Types.h"
18+
#include "swift/Basic/Statistic.h"
19+
#include "llvm/ADT/FoldingSet.h"
20+
#include "llvm/Support/Allocator.h"
21+
#include "RewriteSystem.h"
22+
23+
namespace swift {
24+
25+
namespace rewriting {
26+
27+
/// A global object that can be shared by multiple rewrite systems.
28+
///
29+
/// It stores uniqued atoms and terms.
30+
///
31+
/// Out-of-line methods are documented in RewriteContext.cpp.
32+
class RewriteContext final {
33+
friend class Atom;
34+
friend class Term;
35+
36+
/// Allocator for uniquing atoms and terms.
37+
llvm::BumpPtrAllocator Allocator;
38+
39+
/// Folding set for uniquing atoms.
40+
llvm::FoldingSet<Atom::Storage> Atoms;
41+
42+
/// Folding set for uniquing terms.
43+
llvm::FoldingSet<Term::Storage> Terms;
44+
45+
RewriteContext(const RewriteContext &) = delete;
46+
RewriteContext(RewriteContext &&) = delete;
47+
RewriteContext &operator=(const RewriteContext &) = delete;
48+
RewriteContext &operator=(RewriteContext &&) = delete;
49+
50+
ASTContext &Context;
51+
52+
public:
53+
/// Statistical counters.
54+
UnifiedStatsReporter *Stats;
55+
56+
RewriteContext(ASTContext &ctx) : Context(ctx), Stats(ctx.Stats) {}
57+
58+
Term getTermForType(CanType paramType, const ProtocolDecl *proto);
59+
60+
MutableTerm getMutableTermForType(CanType paramType,
61+
const ProtocolDecl *proto);
62+
63+
ASTContext &getASTContext() { return Context; }
64+
65+
Type getTypeForTerm(Term term,
66+
TypeArrayView<GenericTypeParamType> genericParams,
67+
const ProtocolGraph &protos) const;
68+
69+
Type getTypeForTerm(const MutableTerm &term,
70+
TypeArrayView<GenericTypeParamType> genericParams,
71+
const ProtocolGraph &protos) const;
72+
73+
Type getRelativeTypeForTerm(
74+
const MutableTerm &term, const MutableTerm &prefix,
75+
const ProtocolGraph &protos) const;
76+
};
77+
78+
} // end namespace rewriting
79+
80+
} // end namespace swift
81+
82+
#endif

0 commit comments

Comments
 (0)