Skip to content

Commit ff5d0e1

Browse files
committed
RequirementMachine: Split off RuleBuilder into a new RequirementLowering.cpp file
1 parent d164fb8 commit ff5d0e1

File tree

4 files changed

+402
-327
lines changed

4 files changed

+402
-327
lines changed

lib/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ add_swift_host_library(swiftAST STATIC
8080
RequirementMachine/KnuthBendix.cpp
8181
RequirementMachine/PropertyMap.cpp
8282
RequirementMachine/PropertyUnification.cpp
83+
RequirementMachine/RequirementLowering.cpp
8384
RequirementMachine/RequirementMachine.cpp
8485
RequirementMachine/RequirementMachineRequests.cpp
8586
RequirementMachine/RewriteContext.cpp
Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
//===--- RequirementLowering.cpp - Building rules from requirements -------===//
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+
// This file implements logic for lowering generic requirements to rewrite rules
14+
// in the requirement machine.
15+
//
16+
// This includes generic requirements from canonical generic signatures and
17+
// protocol requirement signatures, as well as user-written requirements in
18+
// protocols ("structural requirements") and the 'where' clauses of generic
19+
// declarations.
20+
//
21+
// There is some additional desugaring logic for user-written requirements.
22+
//
23+
//===----------------------------------------------------------------------===//
24+
25+
#include "RequirementLowering.h"
26+
#include "swift/AST/ASTContext.h"
27+
#include "swift/AST/Decl.h"
28+
#include "swift/AST/Requirement.h"
29+
#include "llvm/ADT/SmallVector.h"
30+
#include "RewriteContext.h"
31+
#include "RewriteSystem.h"
32+
#include "Symbol.h"
33+
#include "Term.h"
34+
35+
using namespace swift;
36+
using namespace rewriting;
37+
38+
/// Given a concrete type that may contain type parameters in structural positions,
39+
/// collect all the structural type parameter components, and replace them all with
40+
/// fresh generic parameters. The fresh generic parameters all have a depth of 0,
41+
/// and the index is an index into the 'result' array.
42+
///
43+
/// For example, given the concrete type Foo<X.Y, Array<Z>>, this produces the
44+
/// result type Foo<τ_0_0, Array<τ_0_1>>, with result array {X.Y, Z}.
45+
CanType
46+
RuleBuilder::getConcreteSubstitutionSchema(CanType concreteType,
47+
const ProtocolDecl *proto,
48+
SmallVectorImpl<Term> &result) {
49+
assert(!concreteType->isTypeParameter() && "Must have a concrete type here");
50+
51+
if (!concreteType->hasTypeParameter())
52+
return concreteType;
53+
54+
return CanType(concreteType.transformRec([&](Type t) -> Optional<Type> {
55+
if (!t->isTypeParameter())
56+
return None;
57+
58+
unsigned index = result.size();
59+
result.push_back(Context.getTermForType(CanType(t), proto));
60+
61+
return CanGenericTypeParamType::get(/*type sequence=*/ false,
62+
/*depth=*/0, index,
63+
Context.getASTContext());
64+
}));
65+
}
66+
67+
void RuleBuilder::addRequirements(ArrayRef<Requirement> requirements) {
68+
// Collect all protocols transitively referenced from these requirements.
69+
for (auto req : requirements) {
70+
if (req.getKind() == RequirementKind::Conformance) {
71+
addProtocol(req.getProtocolDecl(), /*initialComponent=*/false);
72+
}
73+
}
74+
75+
collectRulesFromReferencedProtocols();
76+
77+
// Add rewrite rules for all top-level requirements.
78+
for (const auto &req : requirements)
79+
addRequirement(req, /*proto=*/nullptr);
80+
}
81+
82+
void RuleBuilder::addProtocols(ArrayRef<const ProtocolDecl *> protos) {
83+
// Collect all protocols transitively referenced from this connected component
84+
// of the protocol dependency graph.
85+
for (auto proto : protos) {
86+
addProtocol(proto, /*initialComponent=*/true);
87+
}
88+
89+
collectRulesFromReferencedProtocols();
90+
}
91+
92+
/// For an associated type T in a protocol P, we add a rewrite rule:
93+
///
94+
/// [P].T => [P:T]
95+
///
96+
/// Intuitively, this means "if a type conforms to P, it has a nested type
97+
/// named T".
98+
void RuleBuilder::addAssociatedType(const AssociatedTypeDecl *type,
99+
const ProtocolDecl *proto) {
100+
MutableTerm lhs;
101+
lhs.add(Symbol::forProtocol(proto, Context));
102+
lhs.add(Symbol::forName(type->getName(), Context));
103+
104+
MutableTerm rhs;
105+
rhs.add(Symbol::forAssociatedType(proto, type->getName(), Context));
106+
107+
PermanentRules.emplace_back(lhs, rhs);
108+
}
109+
110+
/// Lowers a generic requirement to a rewrite rule.
111+
///
112+
/// If \p proto is null, this is a generic requirement from the top-level
113+
/// generic signature. The added rewrite rule will be rooted in a generic
114+
/// parameter symbol.
115+
///
116+
/// If \p proto is non-null, this is a generic requirement in the protocol's
117+
/// requirement signature. The added rewrite rule will be rooted in a
118+
/// protocol symbol.
119+
void RuleBuilder::addRequirement(const Requirement &req,
120+
const ProtocolDecl *proto) {
121+
if (Dump) {
122+
llvm::dbgs() << "+ ";
123+
req.dump(llvm::dbgs());
124+
llvm::dbgs() << "\n";
125+
}
126+
127+
// Compute the left hand side.
128+
auto subjectType = CanType(req.getFirstType());
129+
auto subjectTerm = Context.getMutableTermForType(subjectType, proto);
130+
131+
// Compute the right hand side.
132+
MutableTerm constraintTerm;
133+
134+
switch (req.getKind()) {
135+
case RequirementKind::Conformance: {
136+
// A conformance requirement T : P becomes a rewrite rule
137+
//
138+
// T.[P] == T
139+
//
140+
// Intuitively, this means "any type ending with T conforms to P".
141+
auto *proto = req.getProtocolDecl();
142+
143+
constraintTerm = subjectTerm;
144+
constraintTerm.add(Symbol::forProtocol(proto, Context));
145+
break;
146+
}
147+
148+
case RequirementKind::Superclass: {
149+
// A superclass requirement T : C<X, Y> becomes a rewrite rule
150+
//
151+
// T.[superclass: C<X, Y>] => T
152+
//
153+
// Together with a rewrite rule
154+
//
155+
// [superclass: C<X, Y>].[layout: L] => [superclass: C<X, Y>]
156+
//
157+
// Where 'L' is either AnyObject or _NativeObject, depending on the
158+
// ancestry of C.
159+
//
160+
// The second rule is marked permanent. Completion will derive a new
161+
// rule as a consequence of these two rules:
162+
//
163+
// T.[layout: L] => T
164+
//
165+
// The new rule will be marked redundant by homotopy reduction since
166+
// it is a consequence of the other two rules.
167+
auto otherType = CanType(req.getSecondType());
168+
169+
// Build the symbol [superclass: C<X, Y>].
170+
SmallVector<Term, 1> substitutions;
171+
otherType = getConcreteSubstitutionSchema(otherType, proto,
172+
substitutions);
173+
auto superclassSymbol = Symbol::forSuperclass(otherType, substitutions,
174+
Context);
175+
176+
{
177+
// Build the symbol [layout: L].
178+
auto layout =
179+
LayoutConstraint::getLayoutConstraint(
180+
otherType->getClassOrBoundGenericClass()->usesObjCObjectModel()
181+
? LayoutConstraintKind::Class
182+
: LayoutConstraintKind::NativeClass,
183+
Context.getASTContext());
184+
auto layoutSymbol = Symbol::forLayout(layout, Context);
185+
186+
MutableTerm layoutSubjectTerm;
187+
layoutSubjectTerm.add(superclassSymbol);
188+
189+
MutableTerm layoutConstraintTerm = layoutSubjectTerm;
190+
layoutConstraintTerm.add(layoutSymbol);
191+
192+
// Add the rule [superclass: C<X, Y>].[layout: L] => [superclass: C<X, Y>].
193+
PermanentRules.emplace_back(layoutConstraintTerm,
194+
layoutSubjectTerm);
195+
}
196+
197+
// Build the term T.[superclass: C<X, Y>].
198+
constraintTerm = subjectTerm;
199+
constraintTerm.add(superclassSymbol);
200+
break;
201+
}
202+
203+
case RequirementKind::Layout: {
204+
// A layout requirement T : L becomes a rewrite rule
205+
//
206+
// T.[layout: L] == T
207+
constraintTerm = subjectTerm;
208+
constraintTerm.add(Symbol::forLayout(req.getLayoutConstraint(),
209+
Context));
210+
break;
211+
}
212+
213+
case RequirementKind::SameType: {
214+
auto otherType = CanType(req.getSecondType());
215+
216+
if (!otherType->isTypeParameter()) {
217+
// A concrete same-type requirement T == C<X, Y> becomes a
218+
// rewrite rule
219+
//
220+
// T.[concrete: C<X, Y>] => T
221+
SmallVector<Term, 1> substitutions;
222+
otherType = getConcreteSubstitutionSchema(otherType, proto,
223+
substitutions);
224+
225+
constraintTerm = subjectTerm;
226+
constraintTerm.add(Symbol::forConcreteType(otherType, substitutions,
227+
Context));
228+
break;
229+
}
230+
231+
constraintTerm = Context.getMutableTermForType(otherType, proto);
232+
break;
233+
}
234+
}
235+
236+
RequirementRules.emplace_back(subjectTerm, constraintTerm);
237+
}
238+
239+
/// Record information about a protocol if we have no seen it yet.
240+
void RuleBuilder::addProtocol(const ProtocolDecl *proto,
241+
bool initialComponent) {
242+
if (ProtocolMap.count(proto) > 0)
243+
return;
244+
245+
ProtocolMap[proto] = initialComponent;
246+
Protocols.push_back(proto);
247+
}
248+
249+
/// Compute the transitive closure of the set of all protocols referenced from
250+
/// the right hand sides of conformance requirements, and convert their
251+
/// requirements to rewrite rules.
252+
void RuleBuilder::collectRulesFromReferencedProtocols() {
253+
unsigned i = 0;
254+
while (i < Protocols.size()) {
255+
auto *proto = Protocols[i++];
256+
for (auto *depProto : proto->getProtocolDependencies()) {
257+
addProtocol(depProto, /*initialComponent=*/false);
258+
}
259+
}
260+
261+
// Add rewrite rules for each protocol.
262+
for (auto *proto : Protocols) {
263+
if (Dump) {
264+
llvm::dbgs() << "protocol " << proto->getName() << " {\n";
265+
}
266+
267+
MutableTerm lhs;
268+
lhs.add(Symbol::forProtocol(proto, Context));
269+
lhs.add(Symbol::forProtocol(proto, Context));
270+
271+
MutableTerm rhs;
272+
rhs.add(Symbol::forProtocol(proto, Context));
273+
274+
PermanentRules.emplace_back(lhs, rhs);
275+
276+
for (auto *assocType : proto->getAssociatedTypeMembers())
277+
addAssociatedType(assocType, proto);
278+
279+
for (auto *inheritedProto : Context.getInheritedProtocols(proto)) {
280+
for (auto *assocType : inheritedProto->getAssociatedTypeMembers())
281+
addAssociatedType(assocType, proto);
282+
}
283+
284+
// If this protocol is part of the initial connected component, we're
285+
// building requirement signatures for all protocols in this component,
286+
// and so we must start with the structural requirements.
287+
//
288+
// Otherwise, we should either already have a requirement signature, or
289+
// we can trigger the computation of the requirement signatures of the
290+
// next component recursively.
291+
if (ProtocolMap[proto]) {
292+
for (auto req : proto->getStructuralRequirements())
293+
addRequirement(req.req.getCanonical(), proto);
294+
} else {
295+
for (auto req : proto->getRequirementSignature())
296+
addRequirement(req.getCanonical(), proto);
297+
}
298+
299+
if (Dump) {
300+
llvm::dbgs() << "}\n";
301+
}
302+
}
303+
}

0 commit comments

Comments
 (0)