Skip to content

Commit 66cf913

Browse files
committed
RequirementMachine: Minimization understands 'relations' among layout requirements
A superclass requirement 'T : C' implies a layout requirement 'T : AnyObject' (if the class is @objc) or 'T : _NativeObject' (if the class is not @objc). In the latter case, there might already be a 'T : AnyObject' requirement, in which case the type parameter 'T' is subject to two layout requirements: T : AnyObject T : _NativeObject The second requirement implies the first however. To encode this in the world of rewrite loops, we the notion of a 'relation' between property symbols, and a 'Relation' rewrite step. Here, the relation is that _NativeObject < AnyObject. Once this relation is recorded, the Relation rewrite step will transform T.[layout: _NativeObject].[layout: AnyObject] into T.[layout: _NativeObject] and vice versa. This rewrite step allows us to construct a rewrite loop which makes the first rewrite rule redundant via the second.
1 parent 7148aea commit 66cf913

File tree

9 files changed

+206
-11
lines changed

9 files changed

+206
-11
lines changed

lib/AST/RequirementMachine/HomotopyReduction.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ RewriteLoop::findRulesAppearingOnceInEmptyContext(
9797
case RewriteStep::AdjustConcreteType:
9898
case RewriteStep::Shift:
9999
case RewriteStep::Decompose:
100+
case RewriteStep::Relation:
100101
case RewriteStep::ConcreteConformance:
101102
case RewriteStep::SuperclassConformance:
102103
case RewriteStep::ConcreteTypeWitness:
@@ -220,6 +221,7 @@ RewritePath RewritePath::splitCycleAtRule(unsigned ruleID) const {
220221
case RewriteStep::AdjustConcreteType:
221222
case RewriteStep::Shift:
222223
case RewriteStep::Decompose:
224+
case RewriteStep::Relation:
223225
case RewriteStep::ConcreteConformance:
224226
case RewriteStep::SuperclassConformance:
225227
case RewriteStep::ConcreteTypeWitness:
@@ -321,6 +323,7 @@ bool RewritePath::replaceRuleWithPath(unsigned ruleID,
321323
case RewriteStep::AdjustConcreteType:
322324
case RewriteStep::Shift:
323325
case RewriteStep::Decompose:
326+
case RewriteStep::Relation:
324327
case RewriteStep::ConcreteConformance:
325328
case RewriteStep::SuperclassConformance:
326329
case RewriteStep::ConcreteTypeWitness:

lib/AST/RequirementMachine/MinimalConformances.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ void RewriteLoop::findProtocolConformanceRules(
137137
case RewriteStep::AdjustConcreteType:
138138
case RewriteStep::Shift:
139139
case RewriteStep::Decompose:
140+
case RewriteStep::Relation:
140141
case RewriteStep::ConcreteConformance:
141142
case RewriteStep::SuperclassConformance:
142143
case RewriteStep::ConcreteTypeWitness:

lib/AST/RequirementMachine/PropertyMap.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ Optional<unsigned> PropertyMap::addProperty(
319319
assert(*System.getRule(ruleID).isPropertyRule() == property);
320320
auto *props = getOrCreateProperties(key);
321321
bool debug = Debug.contains(DebugFlags::ConcreteUnification);
322-
return props->addProperty(property, ruleID, Context,
322+
return props->addProperty(property, ruleID, System,
323323
inducedRules, debug);
324324
}
325325

lib/AST/RequirementMachine/PropertyMap.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ class PropertyBag {
103103

104104
Optional<unsigned> addProperty(Symbol property,
105105
unsigned ruleID,
106-
RewriteContext &ctx,
106+
RewriteSystem &system,
107107
SmallVectorImpl<InducedRule> &inducedRules,
108108
bool debug);
109109
void copyPropertiesFrom(const PropertyBag *next,

lib/AST/RequirementMachine/PropertyUnification.cpp

Lines changed: 108 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,82 @@
3737
using namespace swift;
3838
using namespace rewriting;
3939

40+
unsigned RewriteSystem::recordRelation(Symbol lhs, Symbol rhs) {
41+
auto key = std::make_pair(lhs, rhs);
42+
auto found = RelationMap.find(key);
43+
if (found != RelationMap.end())
44+
return found->second;
45+
46+
unsigned index = Relations.size();
47+
Relations.push_back(key);
48+
auto inserted = RelationMap.insert(std::make_pair(key, index));
49+
assert(inserted.second);
50+
(void) inserted;
51+
52+
return index;
53+
}
54+
55+
RewriteSystem::Relation
56+
RewriteSystem::getRelation(unsigned index) const {
57+
return Relations[index];
58+
}
59+
60+
/// Given two property rules (T.[p1] => T) and (T.[p2] => T) where [p1] < [p2],
61+
/// record a rewrite loop that makes the second rule redundant from the first.
62+
static void recordRelation(unsigned lhsRuleID, unsigned rhsRuleID,
63+
RewriteSystem &system,
64+
SmallVectorImpl<InducedRule> &inducedRules,
65+
bool debug) {
66+
const auto &lhsRule = system.getRule(lhsRuleID);
67+
const auto &rhsRule = system.getRule(rhsRuleID);
68+
69+
auto lhsProperty = lhsRule.getLHS().back();
70+
auto rhsProperty = rhsRule.getLHS().back();
71+
72+
assert(lhsProperty.isProperty());
73+
assert(rhsProperty.isProperty());
74+
assert(lhsProperty.getKind() == rhsProperty.getKind());
75+
76+
if (debug) {
77+
llvm::dbgs() << "%% Recording relation: ";
78+
llvm::dbgs() << lhsRule.getLHS() << " < " << rhsProperty << "\n";
79+
}
80+
81+
unsigned relationID = system.recordRelation(lhsProperty, rhsProperty);
82+
83+
/// Build the following rewrite path:
84+
///
85+
/// (T => T.[p1]).[p2] ⊗ T.Relation([p1].[p2] => [p1]) ⊗ (T.[p1] => T).
86+
///
87+
RewritePath path;
88+
89+
/// Starting from T.[p2], the LHS rule in reverse to get T.[p1].[p2].
90+
path.add(RewriteStep::forRewriteRule(/*startOffset=*/0,
91+
/*endOffset=*/1,
92+
/*ruleID=*/lhsRuleID,
93+
/*inverse=*/true));
94+
95+
/// T.Relation([p1].[p2] => [p1]).
96+
path.add(RewriteStep::forRelation(relationID, /*inverse=*/false));
97+
98+
/// (T.[p1] => T).
99+
path.add(RewriteStep::forRewriteRule(/*startOffset=*/0,
100+
/*endOffset=*/0,
101+
/*ruleID=*/lhsRuleID,
102+
/*inverse=*/false));
103+
104+
/// Add the rule (T.[p2] => T) with the above rewrite path.
105+
///
106+
/// Since a rule (T.[p2] => T) *already exists*, both sides of the new
107+
/// rule will simplify down to T, and the rewrite path will become a loop.
108+
///
109+
/// This loop encodes the fact that (T.[p1] => T) makes (T.[p2] => T)
110+
/// redundant.
111+
inducedRules.emplace_back(MutableTerm(rhsRule.getLHS()),
112+
MutableTerm(rhsRule.getRHS()),
113+
path);
114+
}
115+
40116
/// This method takes a concrete type that was derived from a concrete type
41117
/// produced by RewriteSystemBuilder::getConcreteSubstitutionSchema(),
42118
/// either by extracting a structural sub-component or performing a (Swift AST)
@@ -310,7 +386,7 @@ static std::pair<Symbol, bool> unifySuperclasses(
310386
/// Returns the old conflicting rule ID if there was a conflict,
311387
/// otherwise returns None.
312388
Optional<unsigned> PropertyBag::addProperty(
313-
Symbol property, unsigned ruleID, RewriteContext &ctx,
389+
Symbol property, unsigned ruleID, RewriteSystem &system,
314390
SmallVectorImpl<InducedRule> &inducedRules,
315391
bool debug) {
316392

@@ -320,18 +396,41 @@ Optional<unsigned> PropertyBag::addProperty(
320396
ConformsToRules.push_back(ruleID);
321397
return None;
322398

323-
case Symbol::Kind::Layout:
399+
case Symbol::Kind::Layout: {
400+
auto newLayout = property.getLayoutConstraint();
401+
324402
if (!Layout) {
325-
Layout = property.getLayoutConstraint();
403+
// If we haven't seen a layout requirement before, just record it.
404+
Layout = newLayout;
326405
LayoutRule = ruleID;
327406
} else {
407+
// Otherwise, compute the intersection.
328408
assert(LayoutRule.hasValue());
329-
Layout = Layout.merge(property.getLayoutConstraint());
330-
if (!Layout->isKnownLayout())
409+
auto mergedLayout = Layout.merge(property.getLayoutConstraint());
410+
411+
// If the intersection is invalid, we have a conflict.
412+
if (!mergedLayout->isKnownLayout())
331413
return LayoutRule;
414+
415+
// If the intersection is equal to the existing layout requirement,
416+
// the new layout requirement is redundant.
417+
if (mergedLayout == Layout) {
418+
recordRelation(*LayoutRule, ruleID, system, inducedRules, debug);
419+
420+
// If the intersection is equal to the new layout requirement, the
421+
// existing layout requirement is redundant.
422+
} else if (mergedLayout == newLayout) {
423+
recordRelation(ruleID, *LayoutRule, system, inducedRules, debug);
424+
LayoutRule = ruleID;
425+
} else {
426+
llvm::errs() << "Arbitrary intersection of layout requirements is "
427+
<< "supported yet\n";
428+
abort();
429+
}
332430
}
333431

334432
return None;
433+
}
335434

336435
case Symbol::Kind::Superclass: {
337436
// FIXME: Also handle superclass vs concrete
@@ -342,7 +441,8 @@ Optional<unsigned> PropertyBag::addProperty(
342441
} else {
343442
assert(SuperclassRule.hasValue());
344443
auto pair = unifySuperclasses(*Superclass, property,
345-
ctx, inducedRules, debug);
444+
system.getRewriteContext(),
445+
inducedRules, debug);
346446
Superclass = pair.first;
347447
bool conflict = pair.second;
348448
if (conflict)
@@ -359,7 +459,8 @@ Optional<unsigned> PropertyBag::addProperty(
359459
} else {
360460
assert(ConcreteTypeRule.hasValue());
361461
bool conflict = unifyConcreteTypes(*ConcreteType, property,
362-
ctx, inducedRules, debug);
462+
system.getRewriteContext(),
463+
inducedRules, debug);
363464
if (conflict)
364465
return ConcreteTypeRule;
365466
}

lib/AST/RequirementMachine/RewriteLoop.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,19 @@ void RewriteStep::dump(llvm::raw_ostream &out,
6464
out << Arg << ")";
6565
break;
6666
}
67+
case Relation: {
68+
evaluator.applyRelation(*this, system);
69+
70+
auto relation = system.getRelation(Arg);
71+
out << "Relation(";
72+
if (Inverse) {
73+
out << relation.second << " > " << relation.first;
74+
} else {
75+
out << relation.first << " < " << relation.second;
76+
}
77+
out << ")";
78+
break;
79+
}
6780
case ConcreteConformance: {
6881
evaluator.applyConcreteConformance(*this, system);
6982

@@ -280,6 +293,41 @@ void RewritePathEvaluator::applyShift(const RewriteStep &step,
280293
}
281294
}
282295

296+
void RewritePathEvaluator::applyRelation(const RewriteStep &step,
297+
const RewriteSystem &system) {
298+
assert(step.Kind == RewriteStep::Relation);
299+
300+
auto relation = system.getRelation(step.Arg);
301+
auto &term = getCurrentTerm();
302+
303+
if (!step.Inverse) {
304+
// Given a term T.[p1].[p2].U where |U| == EndOffset, build the
305+
// term T.[p1].U.
306+
307+
auto lhsProperty = *(term.end() - step.EndOffset - 2);
308+
auto rhsProperty = *(term.end() - step.EndOffset - 1);
309+
assert(lhsProperty == relation.first);
310+
assert(rhsProperty == relation.second);
311+
312+
MutableTerm result(term.begin(), term.end() - step.EndOffset - 1);
313+
result.append(term.end() - step.EndOffset, term.end());
314+
315+
term = result;
316+
} else {
317+
// Given a term T.[p1].U where |U| == EndOffset, build the
318+
// term T.[p1].[p2].U.
319+
320+
auto lhsProperty = *(term.end() - step.EndOffset - 1);
321+
assert(lhsProperty == relation.first);
322+
323+
MutableTerm result(term.begin(), term.end() - step.EndOffset);
324+
result.add(relation.second);
325+
result.append(term.end() - step.EndOffset, term.end());
326+
327+
term = result;
328+
}
329+
}
330+
283331
void RewritePathEvaluator::applyDecompose(const RewriteStep &step,
284332
const RewriteSystem &system) {
285333
assert(step.Kind == RewriteStep::Decompose);
@@ -623,6 +671,10 @@ void RewritePathEvaluator::apply(const RewriteStep &step,
623671
applyDecompose(step, system);
624672
break;
625673

674+
case RewriteStep::Relation:
675+
applyRelation(step, system);
676+
break;
677+
626678
case RewriteStep::ConcreteConformance:
627679
case RewriteStep::SuperclassConformance:
628680
applyConcreteConformance(step, system);

lib/AST/RequirementMachine/RewriteLoop.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,19 @@ struct RewriteStep {
8787
/// *** Rewrite step kinds introduced by the property map ***
8888
///
8989

90+
/// If not inverted: the top of the primary stack must be a term T.[p1].[p2]
91+
/// ending in a pair of property symbols [p1] and [p2], where [p1] < [p2].
92+
/// The symbol [p2] is dropped, leaving behind the term T.[p1].
93+
///
94+
/// If inverted: the top of the primary stack must be a term T.[p1]
95+
/// ending in a property symbol [p1]. The rewrite system must have a
96+
/// recorded relation for the pair ([p1], [p2]). The symbol [p2] is added
97+
/// to the end of the term, leaving behind the term T.[p1].[p2].
98+
///
99+
/// The Arg field stores the result of calling
100+
/// RewriteSystem::recordRelation().
101+
Relation,
102+
90103
/// If not inverted: the top of the primary stack must be a term ending in
91104
/// a concrete type symbol [concrete: C] followed by a protocol symbol [P].
92105
/// These two symbols are combined into a single concrete conformance
@@ -196,6 +209,11 @@ struct RewriteStep {
196209
/*arg=*/numSubstitutions, inverse);
197210
}
198211

212+
static RewriteStep forRelation(unsigned relationID, bool inverse) {
213+
return RewriteStep(Relation, /*startOffset=*/0, /*endOffset=*/0,
214+
/*arg=*/relationID, inverse);
215+
}
216+
199217
static RewriteStep forConcreteConformance(bool inverse) {
200218
return RewriteStep(ConcreteConformance, /*startOffset=*/0, /*endOffset=*/0,
201219
/*arg=*/0, inverse);
@@ -402,6 +420,9 @@ struct RewritePathEvaluator {
402420
void applyShift(const RewriteStep &step,
403421
const RewriteSystem &system);
404422

423+
void applyRelation(const RewriteStep &step,
424+
const RewriteSystem &system);
425+
405426
void applyDecompose(const RewriteStep &step,
406427
const RewriteSystem &system);
407428

lib/AST/RequirementMachine/RewriteSystem.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,17 @@ class RewriteSystem final {
325325
//////////////////////////////////////////////////////////////////////////////
326326

327327
public:
328+
/// The left hand side is known to be smaller than the right hand side.
329+
using Relation = std::pair<Symbol, Symbol>;
330+
331+
private:
332+
llvm::DenseMap<Relation, unsigned> RelationMap;
333+
std::vector<Relation> Relations;
334+
335+
public:
336+
unsigned recordRelation(Symbol lhs, Symbol rhs);
337+
Relation getRelation(unsigned index) const;
338+
328339
/// A type witness has a subject type, stored in LHS, which takes the form:
329340
///
330341
/// T.[concrete: C : P].[P:X]

test/Generics/superclass_and_layout_requirement.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@
22

33
class C {}
44

5-
// CHECK: superclass_and_layout_requirement.(file).P@
5+
// CHECK: superclass_and_layout_requirement.(file).P1@
66
// CHECK: Requirement signature: <Self where Self.T : C>
7-
protocol P {
7+
protocol P1 {
88
associatedtype T : C
99
}
10+
11+
// CHECK: superclass_and_layout_requirement.(file).P2@
12+
// CHECK: Requirement signature: <Self where Self.T : C>
13+
protocol P2 {
14+
associatedtype T where T : C, T : AnyObject
15+
}

0 commit comments

Comments
 (0)