Skip to content

Commit 3576318

Browse files
committed
RequirementMachine: Refactor construction of requirements from rules
The final step in minimization is building Requirements and ProtocolTypeAliases from the minimal Rules in the RewriteSystem. Move this to a new file and refactor it a bit to handle Requirements and ProtocolTypeAliases more consistently.
1 parent a61f67a commit 3576318

File tree

4 files changed

+388
-274
lines changed

4 files changed

+388
-274
lines changed

lib/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ add_swift_host_library(swiftAST STATIC
8787
RequirementMachine/PropertyMap.cpp
8888
RequirementMachine/PropertyRelations.cpp
8989
RequirementMachine/PropertyUnification.cpp
90+
RequirementMachine/RequirementBuilder.cpp
9091
RequirementMachine/RequirementLowering.cpp
9192
RequirementMachine/RequirementMachine.cpp
9293
RequirementMachine/RequirementMachineRequests.cpp
Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
1+
//===--- RequirementBuilder.cpp - Building requirements from rules --------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 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 the final step in generic signature minimization,
14+
// building requirements from a set of minimal, canonical rewrite rules.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#include "RequirementMachine.h"
19+
#include "swift/AST/Decl.h"
20+
#include "swift/AST/Requirement.h"
21+
#include "swift/AST/RequirementSignature.h"
22+
#include "swift/AST/Types.h"
23+
#include "llvm/ADT/ArrayRef.h"
24+
#include "llvm/ADT/DenseMap.h"
25+
#include "llvm/ADT/SmallVector.h"
26+
#include <vector>
27+
28+
using namespace swift;
29+
using namespace rewriting;
30+
31+
namespace {
32+
33+
/// Represents a set of types related by same-type requirements, and an
34+
/// optional concrete type requirement.
35+
struct ConnectedComponent {
36+
llvm::SmallVector<Type, 2> Members;
37+
llvm::SmallVector<Identifier, 1> Aliases;
38+
Type ConcreteType;
39+
40+
void buildRequirements(Type subjectType,
41+
std::vector<Requirement> &reqs,
42+
std::vector<ProtocolTypeAlias> &aliases);
43+
};
44+
45+
/// Case 1: A set of rewrite rules of the form:
46+
///
47+
/// B => A
48+
/// C => A
49+
/// D => A
50+
///
51+
/// Become a series of same-type requirements
52+
///
53+
/// A == B, B == C, C == D
54+
///
55+
/// Case 2: A set of rewrite rules of the form:
56+
///
57+
/// A.[concrete: X] => A
58+
/// B => A
59+
/// C => A
60+
/// D => A
61+
///
62+
/// Become a series of same-type requirements
63+
///
64+
/// A == X, B == X, C == X, D == X
65+
void ConnectedComponent::buildRequirements(Type subjectType,
66+
std::vector<Requirement> &reqs,
67+
std::vector<ProtocolTypeAlias> &aliases) {
68+
std::sort(Members.begin(), Members.end(),
69+
[](Type first, Type second) -> bool {
70+
return compareDependentTypes(first, second) < 0;
71+
});
72+
73+
if (!ConcreteType) {
74+
for (auto name : Aliases) {
75+
aliases.emplace_back(name, subjectType);
76+
}
77+
78+
for (auto constraintType : Members) {
79+
reqs.emplace_back(RequirementKind::SameType,
80+
subjectType, constraintType);
81+
subjectType = constraintType;
82+
}
83+
84+
// For compatibility with the old GenericSignatureBuilder, drop requirements
85+
// containing ErrorTypes.
86+
} else if (!ConcreteType->hasError()) {
87+
// If there are multiple protocol typealiases in the connected component,
88+
// lower them all to a series of identical concrete-type aliases.
89+
for (auto name : Aliases) {
90+
aliases.emplace_back(name, ConcreteType);
91+
}
92+
93+
// If the most canonical representative in the connected component is an
94+
// unresolved DependentMemberType, it must be of the form 'Self.A'
95+
// where 'A' is an alias. Emit the concrete-type alias itself.
96+
if (auto *memberTy = subjectType->getAs<DependentMemberType>()) {
97+
if (memberTy->getAssocType() == nullptr) {
98+
auto *paramTy = memberTy->getBase()->castTo<GenericTypeParamType>();
99+
assert(paramTy->getDepth() == 0 && paramTy->getIndex() == 0);
100+
(void) paramTy;
101+
102+
aliases.emplace_back(memberTy->getName(), ConcreteType);
103+
104+
assert(Members.empty());
105+
return;
106+
}
107+
}
108+
109+
// Otherwise, the most canonical representative must be a resolved
110+
// associated type. Emit a requirement.
111+
reqs.emplace_back(RequirementKind::SameType,
112+
subjectType, ConcreteType);
113+
114+
// Finally, emit a concrete type requirement for all resolved type members
115+
// of the connected component.
116+
for (auto constraintType : Members) {
117+
reqs.emplace_back(RequirementKind::SameType,
118+
constraintType, ConcreteType);
119+
}
120+
}
121+
}
122+
123+
/// Once we're done with minimization, we turn the minimal rules into requirements.
124+
/// This is in a sense the inverse of RuleBuilder in RequirementLowering.cpp.
125+
class RequirementBuilder {
126+
// Input parameters.
127+
const RewriteSystem &System;
128+
const PropertyMap &Map;
129+
TypeArrayView<GenericTypeParamType> GenericParams;
130+
bool Debug;
131+
132+
// Temporary state populated by addRequirementRules() and
133+
// addTypeAliasRules().
134+
llvm::SmallDenseMap<TypeBase *, ConnectedComponent> Components;
135+
136+
public:
137+
// Results.
138+
std::vector<Requirement> Reqs;
139+
std::vector<ProtocolTypeAlias> Aliases;
140+
141+
RequirementBuilder(const RewriteSystem &system, const PropertyMap &map,
142+
TypeArrayView<GenericTypeParamType> genericParams)
143+
: System(system), Map(map), GenericParams(genericParams),
144+
Debug(System.getDebugOptions().contains(DebugFlags::Minimization)) {}
145+
146+
void addRequirementRules(ArrayRef<unsigned> rules);
147+
void addTypeAliasRules(ArrayRef<unsigned> rules);
148+
149+
void processConnectedComponents();
150+
151+
void sortRequirements();
152+
void sortTypeAliases();
153+
};
154+
155+
} // end namespace
156+
157+
void RequirementBuilder::addRequirementRules(ArrayRef<unsigned> rules) {
158+
// Convert a rewrite rule into a requirement.
159+
auto createRequirementFromRule = [&](const Rule &rule) {
160+
if (auto prop = rule.isPropertyRule()) {
161+
auto subjectType = Map.getTypeForTerm(rule.getRHS(), GenericParams);
162+
163+
switch (prop->getKind()) {
164+
case Symbol::Kind::Protocol:
165+
Reqs.emplace_back(RequirementKind::Conformance,
166+
subjectType,
167+
prop->getProtocol()->getDeclaredInterfaceType());
168+
return;
169+
170+
case Symbol::Kind::Layout:
171+
Reqs.emplace_back(RequirementKind::Layout,
172+
subjectType,
173+
prop->getLayoutConstraint());
174+
return;
175+
176+
case Symbol::Kind::Superclass: {
177+
// Requirements containing unresolved name symbols originate from
178+
// invalid code and should not appear in the generic signature.
179+
for (auto term : prop->getSubstitutions()) {
180+
if (term.containsUnresolvedSymbols())
181+
return;
182+
}
183+
184+
// Requirements containing error types originate from invalid code
185+
// and should not appear in the generic signature.
186+
if (prop->getConcreteType()->hasError())
187+
return;
188+
189+
auto superclassType = Map.getTypeFromSubstitutionSchema(
190+
prop->getConcreteType(),
191+
prop->getSubstitutions(),
192+
GenericParams, MutableTerm());
193+
194+
Reqs.emplace_back(RequirementKind::Superclass,
195+
subjectType, superclassType);
196+
return;
197+
}
198+
199+
case Symbol::Kind::ConcreteType: {
200+
// Requirements containing unresolved name symbols originate from
201+
// invalid code and should not appear in the generic signature.
202+
for (auto term : prop->getSubstitutions()) {
203+
if (term.containsUnresolvedSymbols())
204+
return;
205+
}
206+
207+
// Requirements containing error types originate from invalid code
208+
// and should not appear in the generic signature.
209+
if (prop->getConcreteType()->hasError())
210+
return;
211+
212+
auto concreteType = Map.getTypeFromSubstitutionSchema(
213+
prop->getConcreteType(),
214+
prop->getSubstitutions(),
215+
GenericParams, MutableTerm());
216+
217+
auto &component = Components[subjectType.getPointer()];
218+
assert(!component.ConcreteType);
219+
component.ConcreteType = concreteType;
220+
return;
221+
}
222+
223+
case Symbol::Kind::ConcreteConformance:
224+
// "Concrete conformance requirements" are not recorded in the generic
225+
// signature.
226+
return;
227+
228+
case Symbol::Kind::Name:
229+
case Symbol::Kind::AssociatedType:
230+
case Symbol::Kind::GenericParam:
231+
break;
232+
}
233+
234+
llvm_unreachable("Invalid symbol kind");
235+
}
236+
237+
assert(rule.getLHS().back().getKind() != Symbol::Kind::Protocol);
238+
auto constraintType = Map.getTypeForTerm(rule.getLHS(), GenericParams);
239+
auto subjectType = Map.getTypeForTerm(rule.getRHS(), GenericParams);
240+
241+
Components[subjectType.getPointer()].Members.push_back(constraintType);
242+
};
243+
244+
if (Debug) {
245+
llvm::dbgs() << "\nMinimized rules:\n";
246+
}
247+
248+
// Build the list of requirements, storing same-type requirements off
249+
// to the side.
250+
for (unsigned ruleID : rules) {
251+
const auto &rule = System.getRule(ruleID);
252+
253+
if (Debug) {
254+
llvm::dbgs() << "- " << rule << "\n";
255+
}
256+
257+
createRequirementFromRule(rule);
258+
}
259+
}
260+
261+
void RequirementBuilder::addTypeAliasRules(ArrayRef<unsigned> rules) {
262+
for (unsigned ruleID : rules) {
263+
const auto &rule = System.getRule(ruleID);
264+
auto name = *rule.isProtocolTypeAliasRule();
265+
Type underlyingType;
266+
267+
auto subjectType = Map.getTypeForTerm(rule.getRHS(), GenericParams);
268+
269+
if (auto prop = rule.isPropertyRule()) {
270+
assert(prop->getKind() == Symbol::Kind::ConcreteType);
271+
272+
// Requirements containing unresolved name symbols originate from
273+
// invalid code and should not appear in the generic signature.
274+
for (auto term : prop->getSubstitutions()) {
275+
if (term.containsUnresolvedSymbols())
276+
continue;
277+
}
278+
279+
// Requirements containing error types originate from invalid code
280+
// and should not appear in the generic signature.
281+
if (prop->getConcreteType()->hasError())
282+
continue;
283+
284+
auto concreteType = Map.getTypeFromSubstitutionSchema(
285+
prop->getConcreteType(),
286+
prop->getSubstitutions(),
287+
GenericParams, MutableTerm());
288+
auto &component = Components[subjectType.getPointer()];
289+
assert(!component.ConcreteType);
290+
Components[subjectType.getPointer()].ConcreteType = concreteType;
291+
} else {
292+
Components[subjectType.getPointer()].Aliases.push_back(name);
293+
}
294+
}
295+
}
296+
297+
void RequirementBuilder::processConnectedComponents() {
298+
// Now, convert each connected component into a series of same-type
299+
// requirements.
300+
for (auto &pair : Components) {
301+
pair.second.buildRequirements(pair.first, Reqs, Aliases);
302+
}
303+
}
304+
305+
void RequirementBuilder::sortRequirements() {
306+
llvm::array_pod_sort(Reqs.begin(), Reqs.end(),
307+
[](const Requirement *lhs, const Requirement *rhs) -> int {
308+
return lhs->compare(*rhs);
309+
});
310+
311+
if (Debug) {
312+
llvm::dbgs() << "Requirements:\n";
313+
for (const auto &req : Reqs) {
314+
req.dump(llvm::dbgs());
315+
llvm::dbgs() << "\n";
316+
}
317+
}
318+
}
319+
320+
void RequirementBuilder::sortTypeAliases() {
321+
llvm::array_pod_sort(Aliases.begin(), Aliases.end(),
322+
[](const ProtocolTypeAlias *lhs,
323+
const ProtocolTypeAlias *rhs) -> int {
324+
return lhs->getName().compare(rhs->getName());
325+
});
326+
327+
if (Debug) {
328+
llvm::dbgs() << "\nMinimized type aliases:\n";
329+
for (const auto &alias : Aliases) {
330+
PrintOptions opts;
331+
opts.ProtocolQualifiedDependentMemberTypes = true;
332+
333+
llvm::dbgs() << "- " << alias.getName() << " == ";
334+
alias.getUnderlyingType().print(llvm::dbgs(), opts);
335+
llvm::dbgs() << "\n";
336+
}
337+
}
338+
}
339+
340+
/// Convert a list of non-permanent, non-redundant rewrite rules into a list of
341+
/// requirements sorted in canonical order. The \p genericParams are used to
342+
/// produce sugared types.
343+
void
344+
RequirementMachine::buildRequirementsFromRules(
345+
ArrayRef<unsigned> requirementRules,
346+
ArrayRef<unsigned> typeAliasRules,
347+
TypeArrayView<GenericTypeParamType> genericParams,
348+
std::vector<Requirement> &reqs,
349+
std::vector<ProtocolTypeAlias> &aliases) const {
350+
RequirementBuilder builder(System, Map, genericParams);
351+
352+
builder.addRequirementRules(requirementRules);
353+
builder.addTypeAliasRules(typeAliasRules);
354+
builder.processConnectedComponents();
355+
builder.sortRequirements();
356+
builder.sortTypeAliases();
357+
358+
reqs = std::move(builder.Reqs);
359+
aliases = std::move(builder.Aliases);
360+
}

lib/AST/RequirementMachine/RequirementMachine.h

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,12 @@ class RequirementMachine final {
108108

109109
MutableTerm getLongestValidPrefix(const MutableTerm &term) const;
110110

111-
std::vector<Requirement> buildRequirementsFromRules(
112-
ArrayRef<unsigned> rules,
113-
TypeArrayView<GenericTypeParamType> genericParams) const;
114-
115-
std::vector<ProtocolTypeAlias> buildProtocolTypeAliasesFromRules(
116-
ArrayRef<unsigned> rules,
117-
TypeArrayView<GenericTypeParamType> genericParams) const;
111+
void buildRequirementsFromRules(
112+
ArrayRef<unsigned> requirementRules,
113+
ArrayRef<unsigned> typeAliasRules,
114+
TypeArrayView<GenericTypeParamType> genericParams,
115+
std::vector<Requirement> &reqs,
116+
std::vector<ProtocolTypeAlias> &aliases) const;
118117

119118
TypeArrayView<GenericTypeParamType> getGenericParams() const {
120119
return TypeArrayView<GenericTypeParamType>(

0 commit comments

Comments
 (0)