Skip to content

Commit 6177f83

Browse files
committed
[GSB] SE-0157: Reprocess delayed requirements when we need a complete PA.
Whenever we need a complete, well-formed potential archetype, reprocess any delayed requirements, so that we pick up additional requirements on that potential archetype. This relies on us tracking a generation count for the GSB instance as a whole, which gets bumped each time we add some new requirement or create a new potential archetype, and only actually reprocessing delayed requirements when the generation count exceeds the point at which we last processed delayed requirements. This gets the most basic recursive protocol constraint working end-to-end and doesn't seem to break things.
1 parent 8b3889a commit 6177f83

File tree

3 files changed

+83
-4
lines changed

3 files changed

+83
-4
lines changed

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,9 @@ class GenericSignatureBuilder {
559559
void processDelayedRequirements();
560560

561561
private:
562+
/// Bump the generation count due to a change.
563+
void bumpGeneration();
564+
562565
/// Describes the relationship between a given constraint and
563566
/// the canonical constraint of the equivalence class.
564567
enum class ConstraintRelation {

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "llvm/ADT/Statistic.h"
3737
#include "llvm/ADT/STLExtras.h"
3838
#include "llvm/Support/raw_ostream.h"
39+
#include "llvm/Support/SaveAndRestore.h"
3940
#include <algorithm>
4041

4142
using namespace swift;
@@ -72,6 +73,16 @@ STATISTIC(NumArchetypeAnchorCacheHits,
7273
"# of hits in the archetype anchor cache");
7374
STATISTIC(NumArchetypeAnchorCacheMisses,
7475
"# of misses in the archetype anchor cache");
76+
STATISTIC(NumProcessDelayedRequirements,
77+
"# of times we process delayed requirements");
78+
STATISTIC(NumProcessDelayedRequirementsUnchanged,
79+
"# of times we process delayed requirements without change");
80+
STATISTIC(NumDelayedRequirementConcrete,
81+
"Delayed requirements resolved as concrete");
82+
STATISTIC(NumDelayedRequirementResolved,
83+
"Delayed requirements resolved");
84+
STATISTIC(NumDelayedRequirementUnresolved,
85+
"Delayed requirements left unresolved");
7586

7687
struct GenericSignatureBuilder::Implementation {
7788
/// Function used to look up conformances.
@@ -90,6 +101,16 @@ struct GenericSignatureBuilder::Implementation {
90101
/// The set of requirements that have been delayed for some reason.
91102
SmallVector<DelayedRequirement, 4> DelayedRequirements;
92103

104+
/// The generation number, which is incremented whenever we successfully
105+
/// introduce a new constraint.
106+
unsigned Generation = 0;
107+
108+
/// The generation at which we last processed all of the delayed requirements.
109+
unsigned LastProcessedGeneration = 0;
110+
111+
/// Whether we are currently processing delayed requirements.
112+
bool ProcessingDelayedRequirements = false;
113+
93114
#ifndef NDEBUG
94115
/// Whether we've already finalized the builder.
95116
bool finalized = false;
@@ -1399,6 +1420,8 @@ bool PotentialArchetype::addConformance(ProtocolDecl *proto,
13991420
return false;
14001421
}
14011422

1423+
builder.bumpGeneration();
1424+
14021425
// Add the conformance along with this constraint.
14031426
equivClass->conformsTo[proto].push_back({this, proto, source});
14041427
++NumConformanceConstraints;
@@ -1867,6 +1890,15 @@ PotentialArchetype *PotentialArchetype::updateNestedTypeForConformance(
18671890
if (!assocType && !concreteDecl)
18681891
return nullptr;
18691892

1893+
// If we were asked for a complete, well-formed archetype, make sure we
1894+
// process delayed requirements if anything changed.
1895+
SWIFT_DEFER {
1896+
ASTContext &ctx = assocType ? assocType->getASTContext()
1897+
: concreteDecl->getASTContext();
1898+
if (ctx.LangOpts.EnableRecursiveConstraints)
1899+
getBuilder()->processDelayedRequirements();
1900+
};
1901+
18701902
Identifier name = assocType ? assocType->getName() : concreteDecl->getName();
18711903
ProtocolDecl *proto =
18721904
assocType ? assocType->getProtocol()
@@ -1901,6 +1933,8 @@ PotentialArchetype *PotentialArchetype::updateNestedTypeForConformance(
19011933
switch (kind) {
19021934
case ArchetypeResolutionKind::CompleteWellFormed:
19031935
case ArchetypeResolutionKind::WellFormed: {
1936+
builder.bumpGeneration();
1937+
19041938
if (assocType)
19051939
resultPA = new PotentialArchetype(this, assocType);
19061940
else
@@ -2819,6 +2853,8 @@ ConstraintResult GenericSignatureBuilder::addLayoutRequirement(
28192853
return ConstraintResult::Resolved;
28202854
}
28212855

2856+
bumpGeneration();
2857+
28222858
auto pa = resolvedSubject->getPotentialArchetype();
28232859
return addLayoutRequirementDirect(pa, layout, source.getSource(pa));
28242860
}
@@ -2903,6 +2939,8 @@ ConstraintResult GenericSignatureBuilder::addSuperclassRequirementDirect(
29032939
.push_back(ConcreteConstraint{T, superclass, source});
29042940
++NumSuperclassConstraints;
29052941

2942+
bumpGeneration();
2943+
29062944
// Update the equivalence class with the constraint.
29072945
updateSuperclass(T, superclass, source);
29082946
return ConstraintResult::Resolved;
@@ -3062,6 +3100,8 @@ GenericSignatureBuilder::addSameTypeRequirementBetweenArchetypes(
30623100
if (T1 == T2)
30633101
return ConstraintResult::Resolved;
30643102

3103+
bumpGeneration();
3104+
30653105
unsigned nestingDepth1 = T1->getNestingDepth();
30663106
unsigned nestingDepth2 = T2->getNestingDepth();
30673107

@@ -3197,6 +3237,8 @@ ConstraintResult GenericSignatureBuilder::addSameTypeRequirementToConcrete(
31973237
ConcreteConstraint{T, Concrete, Source});
31983238
++NumConcreteTypeConstraints;
31993239

3240+
bumpGeneration();
3241+
32003242
// If we've already been bound to a type, match that type.
32013243
if (equivClass->concreteType) {
32023244
return addSameTypeRequirement(equivClass->concreteType, Concrete, Source,
@@ -3979,8 +4021,27 @@ static GenericSignatureBuilder::UnresolvedType asUnresolvedType(
39794021
}
39804022

39814023
void GenericSignatureBuilder::processDelayedRequirements() {
3982-
bool anySolved = !Impl->DelayedRequirements.empty();
3983-
while (anySolved) {
4024+
// If we're already up-to-date, do nothing.
4025+
if (Impl->Generation == Impl->LastProcessedGeneration) { return; }
4026+
4027+
// If there are no delayed requirements, do nothing.
4028+
if (Impl->DelayedRequirements.empty()) { return; }
4029+
4030+
if (Impl->ProcessingDelayedRequirements) { return; }
4031+
4032+
++NumProcessDelayedRequirements;
4033+
4034+
llvm::SaveAndRestore<bool> processing(Impl->ProcessingDelayedRequirements,
4035+
true);
4036+
bool anyChanges = false;
4037+
SWIFT_DEFER {
4038+
Impl->LastProcessedGeneration = Impl->Generation;
4039+
if (!anyChanges)
4040+
++NumProcessDelayedRequirementsUnchanged;
4041+
};
4042+
4043+
bool anySolved;
4044+
do {
39844045
// Steal the delayed requirements so we can reprocess them.
39854046
anySolved = false;
39864047
auto delayed = std::move(Impl->DelayedRequirements);
@@ -4013,21 +4074,35 @@ void GenericSignatureBuilder::processDelayedRequirements() {
40134074
// Update our state based on what happened.
40144075
switch (reqResult) {
40154076
case ConstraintResult::Concrete:
4077+
++NumDelayedRequirementConcrete;
4078+
anySolved = true;
4079+
break;
4080+
40164081
case ConstraintResult::Conflicting:
40174082
anySolved = true;
40184083
break;
40194084

40204085
case ConstraintResult::Resolved:
4086+
++NumDelayedRequirementResolved;
40214087
anySolved = true;
40224088
break;
40234089

40244090
case ConstraintResult::Unresolved:
40254091
// Add the requirement back.
4092+
++NumDelayedRequirementUnresolved;
40264093
Impl->DelayedRequirements.push_back(req);
40274094
break;
40284095
}
40294096
}
4030-
}
4097+
4098+
if (anySolved) {
4099+
anyChanges = true;
4100+
}
4101+
} while (anySolved);
4102+
}
4103+
4104+
void GenericSignatureBuilder::bumpGeneration() {
4105+
++Impl->Generation;
40314106
}
40324107

40334108
template<typename T>

test/decl/protocol/recursive_requirement_ok.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ protocol P {
99
func testP<T: P>(_ t: T) {
1010
testP(t.assoc)
1111
testP(t.assoc.assoc)
12-
testP(t.assoc.assoc.assoc) // FIXME: expected-error{{argument type 'T.Assoc.Assoc.Assoc' does not conform to expected type 'P'}}
12+
testP(t.assoc.assoc.assoc)
13+
testP(t.assoc.assoc.assoc.assoc.assoc.assoc.assoc)
1314
}

0 commit comments

Comments
 (0)