Skip to content

Commit 177b60a

Browse files
authored
Merge pull request #39749 from slavapestov/rqm-minimize-concrete-type-requirements
RequirementMachine: Preliminary support for minimizing layout, superclass and concrete type requirements
2 parents 92ff0c1 + 25718c7 commit 177b60a

File tree

10 files changed

+239
-193
lines changed

10 files changed

+239
-193
lines changed

lib/AST/RequirementMachine/HomotopyReduction.cpp

Lines changed: 63 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,9 @@ void RewriteSystem::minimizeRewriteSystem() {
778778
loop.normalize(*this);
779779
}
780780

781+
// Check invariants before homotopy reduction.
782+
verifyHomotopyGenerators();
783+
781784
// First pass: Eliminate all redundant rules involving unresolved types.
782785
performHomotopyReduction(/*firstPass=*/true,
783786
/*redundantConformances=*/nullptr);
@@ -798,57 +801,10 @@ void RewriteSystem::minimizeRewriteSystem() {
798801
performHomotopyReduction(/*firstPass=*/false,
799802
/*redundantConformances=*/&redundantConformances);
800803

801-
// Assert if homotopy reduction failed to eliminate a redundant conformance,
802-
// since this suggests a misunderstanding on my part.
803-
for (unsigned ruleID : redundantConformances) {
804-
const auto &rule = getRule(ruleID);
805-
assert(rule.isProtocolConformanceRule() &&
806-
"Redundant conformance is not a conformance rule?");
807-
808-
if (!rule.isRedundant()) {
809-
llvm::errs() << "Homotopy reduction did not eliminate redundant "
810-
<< "conformance?\n";
811-
llvm::errs() << "(#" << ruleID << ") " << rule << "\n\n";
812-
dump(llvm::errs());
813-
abort();
814-
}
815-
}
816-
817-
// Assert if homotopy reduction failed to eliminate a rewrite rule which was
818-
// deleted because either it's left hand side can be reduced by some other
819-
// rule, or because it's right hand side can be reduced further.
820-
for (const auto &rule : Rules) {
821-
// Note that sometimes permanent rules can be simplified, but they can never
822-
// be redundant.
823-
if (rule.isPermanent()) {
824-
if (rule.isRedundant()) {
825-
llvm::errs() << "Permanent rule is redundant: " << rule << "\n\n";
826-
dump(llvm::errs());
827-
abort();
828-
}
829-
830-
continue;
831-
}
832-
833-
if (rule.isRedundant())
834-
continue;
835-
836-
// Simplified rules should be redundant.
837-
if (rule.isSimplified()) {
838-
llvm::errs() << "Simplified rule is not redundant: " << rule << "\n\n";
839-
dump(llvm::errs());
840-
abort();
841-
}
842-
843-
// Rules with unresolved name symbols (other than permanent rules for
844-
// associated type introduction) should be redundant.
845-
if (rule.getLHS().containsUnresolvedSymbols() ||
846-
rule.getRHS().containsUnresolvedSymbols()) {
847-
llvm::errs() << "Unresolved rule is not redundant: " << rule << "\n\n";
848-
dump(llvm::errs());
849-
abort();
850-
}
851-
}
804+
// Check invariants after homotopy reduction.
805+
verifyHomotopyGenerators();
806+
verifyRedundantConformances(redundantConformances);
807+
verifyMinimizedRules();
852808
}
853809

854810
/// Collect all non-permanent, non-redundant rules whose domain is equal to
@@ -897,4 +853,60 @@ void RewriteSystem::verifyHomotopyGenerators() const {
897853
}
898854
}
899855
#endif
856+
}
857+
858+
/// Assert if homotopy reduction failed to eliminate a redundant conformance,
859+
/// since this suggests a misunderstanding on my part.
860+
void RewriteSystem::verifyRedundantConformances(
861+
llvm::DenseSet<unsigned> redundantConformances) const {
862+
for (unsigned ruleID : redundantConformances) {
863+
const auto &rule = getRule(ruleID);
864+
assert(rule.isProtocolConformanceRule() &&
865+
"Redundant conformance is not a conformance rule?");
866+
867+
if (!rule.isRedundant()) {
868+
llvm::errs() << "Homotopy reduction did not eliminate redundant "
869+
<< "conformance?\n";
870+
llvm::errs() << "(#" << ruleID << ") " << rule << "\n\n";
871+
dump(llvm::errs());
872+
abort();
873+
}
874+
}
875+
}
876+
877+
// Assert if homotopy reduction failed to eliminate a rewrite rule it was
878+
// supposed to delete.
879+
void RewriteSystem::verifyMinimizedRules() const {
880+
for (const auto &rule : Rules) {
881+
// Note that sometimes permanent rules can be simplified, but they can never
882+
// be redundant.
883+
if (rule.isPermanent()) {
884+
if (rule.isRedundant()) {
885+
llvm::errs() << "Permanent rule is redundant: " << rule << "\n\n";
886+
dump(llvm::errs());
887+
abort();
888+
}
889+
890+
continue;
891+
}
892+
893+
if (rule.isRedundant())
894+
continue;
895+
896+
// Simplified rules should be redundant.
897+
if (rule.isSimplified()) {
898+
llvm::errs() << "Simplified rule is not redundant: " << rule << "\n\n";
899+
dump(llvm::errs());
900+
abort();
901+
}
902+
903+
// Rules with unresolved name symbols (other than permanent rules for
904+
// associated type introduction) should be redundant.
905+
if (rule.getLHS().containsUnresolvedSymbols() ||
906+
rule.getRHS().containsUnresolvedSymbols()) {
907+
llvm::errs() << "Unresolved rule is not redundant: " << rule << "\n\n";
908+
dump(llvm::errs());
909+
abort();
910+
}
911+
}
900912
}

lib/AST/RequirementMachine/PropertyMap.cpp

Lines changed: 20 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -129,53 +129,6 @@ void PropertyBag::dump(llvm::raw_ostream &out) const {
129129
out << " }";
130130
}
131131

132-
/// Concrete type terms are written in terms of generic parameter types that
133-
/// have a depth of 0, and an index into an array of substitution terms.
134-
///
135-
/// See RewriteSystemBuilder::getConcreteSubstitutionSchema().
136-
static unsigned getGenericParamIndex(Type type) {
137-
auto *paramTy = type->castTo<GenericTypeParamType>();
138-
assert(paramTy->getDepth() == 0);
139-
return paramTy->getIndex();
140-
}
141-
142-
/// Reverses the transformation performed by
143-
/// RewriteSystemBuilder::getConcreteSubstitutionSchema().
144-
static Type getTypeFromSubstitutionSchema(Type schema,
145-
ArrayRef<Term> substitutions,
146-
TypeArrayView<GenericTypeParamType> genericParams,
147-
const MutableTerm &prefix,
148-
const ProtocolGraph &protos,
149-
RewriteContext &ctx) {
150-
assert(!schema->isTypeParameter() && "Must have a concrete type here");
151-
152-
if (!schema->hasTypeParameter())
153-
return schema;
154-
155-
return schema.transformRec([&](Type t) -> Optional<Type> {
156-
if (t->is<GenericTypeParamType>()) {
157-
auto index = getGenericParamIndex(t);
158-
auto substitution = substitutions[index];
159-
160-
// Prepend the prefix of the lookup key to the substitution.
161-
if (prefix.empty()) {
162-
// Skip creation of a new MutableTerm in the case where the
163-
// prefix is empty.
164-
return ctx.getTypeForTerm(substitution, genericParams, protos);
165-
} else {
166-
// Otherwise build a new term by appending the substitution
167-
// to the prefix.
168-
MutableTerm result(prefix);
169-
result.append(substitution);
170-
return ctx.getTypeForTerm(result, genericParams, protos);
171-
}
172-
}
173-
174-
assert(!t->isTypeParameter());
175-
return None;
176-
});
177-
}
178-
179132
/// Given a term \p lookupTerm whose suffix must equal this property bag's
180133
/// key, return a new term with that suffix stripped off. Will be empty if
181134
/// \p lookupTerm exactly equals the key.
@@ -205,10 +158,10 @@ Type PropertyBag::getSuperclassBound(
205158
const ProtocolGraph &protos,
206159
RewriteContext &ctx) const {
207160
MutableTerm prefix = getPrefixAfterStrippingKey(lookupTerm);
208-
return getTypeFromSubstitutionSchema(Superclass->getSuperclass(),
209-
Superclass->getSubstitutions(),
210-
genericParams, prefix,
211-
protos, ctx);
161+
return ctx.getTypeFromSubstitutionSchema(Superclass->getSuperclass(),
162+
Superclass->getSubstitutions(),
163+
genericParams, prefix,
164+
protos);
212165
}
213166

214167
/// Get the concrete type of the term represented by this property bag.
@@ -226,70 +179,10 @@ Type PropertyBag::getConcreteType(
226179
const ProtocolGraph &protos,
227180
RewriteContext &ctx) const {
228181
MutableTerm prefix = getPrefixAfterStrippingKey(lookupTerm);
229-
return getTypeFromSubstitutionSchema(ConcreteType->getConcreteType(),
230-
ConcreteType->getSubstitutions(),
231-
genericParams, prefix,
232-
protos, ctx);
233-
}
234-
235-
/// Computes the term corresponding to a member type access on a substitution.
236-
///
237-
/// The type witness is a type parameter of the form τ_0_n.X.Y.Z,
238-
/// where 'n' is an index into the substitution array.
239-
///
240-
/// If the nth entry in the array is S, this will produce S.X.Y.Z.
241-
///
242-
/// There is a special behavior if the substitution is a term consisting of a
243-
/// single protocol symbol [P]. If the innermost associated type in
244-
/// \p typeWitness is [Q:Foo], the result will be [P:Foo], not [P].[Q:Foo] or
245-
/// [Q:Foo].
246-
static MutableTerm getRelativeTermForType(CanType typeWitness,
247-
ArrayRef<Term> substitutions,
248-
RewriteContext &ctx) {
249-
MutableTerm result;
250-
251-
// Get the substitution S corresponding to τ_0_n.
252-
unsigned index = getGenericParamIndex(typeWitness->getRootGenericParam());
253-
result = MutableTerm(substitutions[index]);
254-
255-
// If the substitution is a term consisting of a single protocol symbol
256-
// [P], save P for later.
257-
const ProtocolDecl *proto = nullptr;
258-
if (result.size() == 1 &&
259-
result[0].getKind() == Symbol::Kind::Protocol) {
260-
proto = result[0].getProtocol();
261-
}
262-
263-
// Collect zero or more member type names in reverse order.
264-
SmallVector<Symbol, 3> symbols;
265-
while (auto memberType = dyn_cast<DependentMemberType>(typeWitness)) {
266-
typeWitness = memberType.getBase();
267-
268-
auto *assocType = memberType->getAssocType();
269-
assert(assocType != nullptr &&
270-
"Conformance checking should not produce unresolved member types");
271-
272-
// If the substitution is a term consisting of a single protocol symbol [P],
273-
// produce [P:Foo] instead of [P].[Q:Foo] or [Q:Foo].
274-
const auto *thisProto = assocType->getProtocol();
275-
if (proto && isa<GenericTypeParamType>(typeWitness)) {
276-
thisProto = proto;
277-
278-
assert(result.size() == 1);
279-
assert(result[0].getKind() == Symbol::Kind::Protocol);
280-
assert(result[0].getProtocol() == proto);
281-
result = MutableTerm();
282-
}
283-
284-
symbols.push_back(Symbol::forAssociatedType(thisProto,
285-
assocType->getName(), ctx));
286-
}
287-
288-
// Add the member type names.
289-
for (auto iter = symbols.rbegin(), end = symbols.rend(); iter != end; ++iter)
290-
result.add(*iter);
291-
292-
return result;
182+
return ctx.getTypeFromSubstitutionSchema(ConcreteType->getConcreteType(),
183+
ConcreteType->getSubstitutions(),
184+
genericParams, prefix,
185+
protos);
293186
}
294187

295188
/// This method takes a concrete type that was derived from a concrete type
@@ -327,7 +220,7 @@ remapConcreteSubstitutionSchema(CanType concreteType,
327220
if (!t->isTypeParameter())
328221
return None;
329222

330-
auto term = getRelativeTermForType(CanType(t), substitutions, ctx);
223+
auto term = ctx.getRelativeTermForType(CanType(t), substitutions);
331224

332225
unsigned newIndex = result.size();
333226
result.push_back(Term::get(term, ctx));
@@ -368,10 +261,10 @@ namespace {
368261

369262
if (firstAbstract && secondAbstract) {
370263
// Both sides are type parameters; add a same-type requirement.
371-
auto lhsTerm = getRelativeTermForType(CanType(firstType),
372-
lhsSubstitutions, ctx);
373-
auto rhsTerm = getRelativeTermForType(CanType(secondType),
374-
rhsSubstitutions, ctx);
264+
auto lhsTerm = ctx.getRelativeTermForType(CanType(firstType),
265+
lhsSubstitutions);
266+
auto rhsTerm = ctx.getRelativeTermForType(CanType(secondType),
267+
rhsSubstitutions);
375268
if (lhsTerm != rhsTerm) {
376269
if (debug) {
377270
llvm::dbgs() << "%% Induced rule " << lhsTerm
@@ -385,8 +278,8 @@ namespace {
385278
if (firstAbstract && !secondAbstract) {
386279
// A type parameter is equated with a concrete type; add a concrete
387280
// type requirement.
388-
auto subjectTerm = getRelativeTermForType(CanType(firstType),
389-
lhsSubstitutions, ctx);
281+
auto subjectTerm = ctx.getRelativeTermForType(CanType(firstType),
282+
lhsSubstitutions);
390283

391284
SmallVector<Term, 3> result;
392285
auto concreteType = remapConcreteSubstitutionSchema(CanType(secondType),
@@ -407,8 +300,8 @@ namespace {
407300
if (!firstAbstract && secondAbstract) {
408301
// A concrete type is equated with a type parameter; add a concrete
409302
// type requirement.
410-
auto subjectTerm = getRelativeTermForType(CanType(secondType),
411-
rhsSubstitutions, ctx);
303+
auto subjectTerm = ctx.getRelativeTermForType(CanType(secondType),
304+
rhsSubstitutions);
412305

413306
SmallVector<Term, 3> result;
414307
auto concreteType = remapConcreteSubstitutionSchema(CanType(firstType),
@@ -915,8 +808,8 @@ void PropertyMap::concretizeNestedTypesFromConcreteParent(
915808
if (!t->isTypeParameter())
916809
return None;
917810

918-
auto term = getRelativeTermForType(t->getCanonicalType(),
919-
substitutions, Context);
811+
auto term = Context.getRelativeTermForType(t->getCanonicalType(),
812+
substitutions);
920813
System.simplify(term);
921814
return Context.getTypeForTerm(term, { }, Protos);
922815
}));
@@ -998,7 +891,7 @@ MutableTerm PropertyMap::computeConstraintTermForTypeWitness(
998891
// where 'n' is an index into the substitution array.
999892
//
1000893
// Add a rule T => S.X.Y...Z, where S is the nth substitution term.
1001-
return getRelativeTermForType(typeWitness, substitutions, Context);
894+
return Context.getRelativeTermForType(typeWitness, substitutions);
1002895
}
1003896

1004897
// The type witness is a concrete type.

lib/AST/RequirementMachine/RequirementMachine.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,6 @@ void RequirementMachine::computeCompletion(RewriteSystem::ValidityPolicy policy)
502502

503503
// Check invariants.
504504
System.verifyRewriteRules(policy);
505-
System.verifyHomotopyGenerators();
506505

507506
// Build the property map, which also performs concrete term
508507
// unification; if this added any new rules, run the completion

0 commit comments

Comments
 (0)