Skip to content

Commit 441fa16

Browse files
committed
RequirementMachine: Splitting concrete equivalence classes in protocol requirement signatures
1 parent ff40f10 commit 441fa16

4 files changed

+178
-78
lines changed

lib/AST/RequirementMachine/RequirementMachineRequests.cpp

+123-70
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@ using namespace rewriting;
4242
/// concrete type requirements.
4343
static bool shouldSplitConcreteEquivalenceClass(
4444
Requirement req,
45+
const ProtocolDecl *proto,
4546
const RequirementMachine *machine) {
4647
return (req.getKind() == RequirementKind::SameType &&
4748
req.getSecondType()->isTypeParameter() &&
48-
machine->isConcreteType(req.getSecondType()));
49+
machine->isConcreteType(req.getSecondType(), proto));
4950
}
5051

5152
/// Returns true if this generic signature contains abstract same-type
@@ -54,9 +55,24 @@ static bool shouldSplitConcreteEquivalenceClass(
5455
/// requirements, and minimize the signature again.
5556
static bool shouldSplitConcreteEquivalenceClasses(
5657
ArrayRef<Requirement> requirements,
58+
const ProtocolDecl *proto,
5759
const RequirementMachine *machine) {
5860
for (auto req : requirements) {
59-
if (shouldSplitConcreteEquivalenceClass(req, machine))
61+
if (shouldSplitConcreteEquivalenceClass(req, proto, machine))
62+
return true;
63+
}
64+
65+
return false;
66+
}
67+
68+
/// Same as the above, but with the requirements of a protocol connected
69+
/// component.
70+
static bool shouldSplitConcreteEquivalenceClasses(
71+
const llvm::DenseMap<const ProtocolDecl *, RequirementSignature> &protos,
72+
const RequirementMachine *machine) {
73+
for (const auto &pair : protos) {
74+
if (shouldSplitConcreteEquivalenceClasses(pair.second.getRequirements(),
75+
pair.first, machine))
6076
return true;
6177
}
6278

@@ -71,14 +87,14 @@ static bool shouldSplitConcreteEquivalenceClasses(
7187
static void splitConcreteEquivalenceClasses(
7288
ASTContext &ctx,
7389
ArrayRef<Requirement> requirements,
90+
const ProtocolDecl *proto,
7491
const RequirementMachine *machine,
7592
TypeArrayView<GenericTypeParamType> genericParams,
7693
SmallVectorImpl<StructuralRequirement> &splitRequirements,
7794
unsigned &attempt) {
7895
unsigned maxAttempts =
7996
ctx.LangOpts.RequirementMachineMaxSplitConcreteEquivClassAttempts;
8097

81-
++attempt;
8298
if (attempt >= maxAttempts) {
8399
llvm::errs() << "Splitting concrete equivalence classes did not "
84100
<< "reach fixed point after " << attempt << " attempts.\n";
@@ -94,9 +110,9 @@ static void splitConcreteEquivalenceClasses(
94110
splitRequirements.clear();
95111

96112
for (auto req : requirements) {
97-
if (shouldSplitConcreteEquivalenceClass(req, machine)) {
113+
if (shouldSplitConcreteEquivalenceClass(req, proto, machine)) {
98114
auto concreteType = machine->getConcreteType(
99-
req.getSecondType(), genericParams);
115+
req.getSecondType(), genericParams, proto);
100116

101117
Requirement firstReq(RequirementKind::SameType,
102118
req.getFirstType(), concreteType);
@@ -111,6 +127,25 @@ static void splitConcreteEquivalenceClasses(
111127
}
112128
}
113129

130+
/// Same as the above, but with the requirements of a protocol connected
131+
/// component.
132+
static void splitConcreteEquivalenceClasses(
133+
ASTContext &ctx,
134+
const llvm::DenseMap<const ProtocolDecl *, RequirementSignature> &protos,
135+
const RequirementMachine *machine,
136+
llvm::DenseMap<const ProtocolDecl *,
137+
SmallVector<StructuralRequirement, 4>> &splitProtos,
138+
unsigned &attempt) {
139+
for (const auto &pair : protos) {
140+
const auto *proto = pair.first;
141+
auto genericParams = proto->getGenericSignature().getGenericParams();
142+
splitConcreteEquivalenceClasses(ctx, pair.second.getRequirements(),
143+
proto, machine, genericParams,
144+
splitProtos[proto],
145+
attempt);
146+
}
147+
}
148+
114149
/// Builds the requirement signatures for each protocol in this strongly
115150
/// connected component.
116151
llvm::DenseMap<const ProtocolDecl *, RequirementSignature>
@@ -195,87 +230,99 @@ RequirementSignatureRequestRQM::evaluate(Evaluator &evaluator,
195230
requirements.push_back({req, SourceLoc(), /*inferred=*/false});
196231
}
197232

198-
// Heap-allocate the requirement machine to save stack space.
199-
std::unique_ptr<RequirementMachine> machine(new RequirementMachine(
200-
ctx.getRewriteContext()));
233+
unsigned attempt = 0;
234+
for (;;) {
235+
// Heap-allocate the requirement machine to save stack space.
236+
std::unique_ptr<RequirementMachine> machine(new RequirementMachine(
237+
ctx.getRewriteContext()));
201238

202-
auto status = machine->initWithProtocolWrittenRequirements(component, protos);
203-
if (status.first != CompletionResult::Success) {
204-
// All we can do at this point is diagnose and give each protocol an empty
205-
// requirement signature.
206-
for (const auto *otherProto : component) {
207-
ctx.Diags.diagnose(otherProto->getLoc(),
208-
diag::requirement_machine_completion_failed,
209-
/*protocol=*/1,
210-
unsigned(status.first));
239+
auto status = machine->initWithProtocolWrittenRequirements(component, protos);
240+
if (status.first != CompletionResult::Success) {
241+
// All we can do at this point is diagnose and give each protocol an empty
242+
// requirement signature.
243+
for (const auto *otherProto : component) {
244+
ctx.Diags.diagnose(otherProto->getLoc(),
245+
diag::requirement_machine_completion_failed,
246+
/*protocol=*/1,
247+
unsigned(status.first));
248+
249+
auto rule = machine->getRuleAsStringForDiagnostics(status.second);
250+
ctx.Diags.diagnose(otherProto->getLoc(),
251+
diag::requirement_machine_completion_rule,
252+
rule);
253+
254+
if (otherProto != proto) {
255+
ctx.evaluator.cacheOutput(
256+
RequirementSignatureRequestRQM{const_cast<ProtocolDecl *>(otherProto)},
257+
RequirementSignature(GenericSignatureErrorFlags::CompletionFailed));
258+
}
259+
}
211260

212-
auto rule = machine->getRuleAsStringForDiagnostics(status.second);
213-
ctx.Diags.diagnose(otherProto->getLoc(),
214-
diag::requirement_machine_completion_rule,
215-
rule);
261+
return RequirementSignature(GenericSignatureErrorFlags::CompletionFailed);
262+
}
216263

217-
if (otherProto != proto) {
218-
ctx.evaluator.cacheOutput(
219-
RequirementSignatureRequestRQM{const_cast<ProtocolDecl *>(otherProto)},
220-
RequirementSignature(GenericSignatureErrorFlags::CompletionFailed));
264+
auto minimalRequirements = machine->computeMinimalProtocolRequirements();
265+
266+
if (!machine->getErrors()) {
267+
if (shouldSplitConcreteEquivalenceClasses(minimalRequirements, machine.get())) {
268+
++attempt;
269+
splitConcreteEquivalenceClasses(ctx, minimalRequirements,
270+
machine.get(), protos, attempt);
271+
continue;
221272
}
222273
}
223274

224-
return RequirementSignature(GenericSignatureErrorFlags::CompletionFailed);
225-
}
226-
227-
auto minimalRequirements = machine->computeMinimalProtocolRequirements();
275+
bool debug = machine->getDebugOptions().contains(DebugFlags::Minimization);
228276

229-
bool debug = machine->getDebugOptions().contains(DebugFlags::Minimization);
277+
// The requirement signature for the actual protocol that the result
278+
// was kicked off with.
279+
Optional<RequirementSignature> result;
230280

231-
// The requirement signature for the actual protocol that the result
232-
// was kicked off with.
233-
Optional<RequirementSignature> result;
281+
if (debug) {
282+
llvm::dbgs() << "\nRequirement signatures:\n";
283+
}
234284

235-
if (debug) {
236-
llvm::dbgs() << "\nRequirement signatures:\n";
237-
}
285+
for (const auto &pair : minimalRequirements) {
286+
auto *otherProto = pair.first;
287+
const auto &reqs = pair.second;
238288

239-
for (const auto &pair : minimalRequirements) {
240-
auto *otherProto = pair.first;
241-
const auto &reqs = pair.second;
289+
// Dump the result if requested.
290+
if (debug) {
291+
llvm::dbgs() << "- Protocol " << otherProto->getName() << ": ";
242292

243-
// Dump the result if requested.
244-
if (debug) {
245-
llvm::dbgs() << "- Protocol " << otherProto->getName() << ": ";
293+
auto sig = GenericSignature::get(
294+
otherProto->getGenericSignature().getGenericParams(),
295+
reqs.getRequirements());
246296

247-
auto sig = GenericSignature::get(
248-
otherProto->getGenericSignature().getGenericParams(),
249-
reqs.getRequirements());
297+
PrintOptions opts;
298+
opts.ProtocolQualifiedDependentMemberTypes = true;
299+
sig.print(llvm::dbgs(), opts);
300+
llvm::dbgs() << "\n";
301+
}
250302

251-
PrintOptions opts;
252-
opts.ProtocolQualifiedDependentMemberTypes = true;
253-
sig.print(llvm::dbgs(), opts);
254-
llvm::dbgs() << "\n";
303+
// Don't call setRequirementSignature() on the original proto; the
304+
// request evaluator will do it for us.
305+
if (otherProto == proto)
306+
result = reqs;
307+
else {
308+
auto temp = reqs;
309+
ctx.evaluator.cacheOutput(
310+
RequirementSignatureRequestRQM{const_cast<ProtocolDecl *>(otherProto)},
311+
std::move(temp));
312+
}
255313
}
256314

257-
// Don't call setRequirementSignature() on the original proto; the
258-
// request evaluator will do it for us.
259-
if (otherProto == proto)
260-
result = reqs;
261-
else {
262-
auto temp = reqs;
263-
ctx.evaluator.cacheOutput(
264-
RequirementSignatureRequestRQM{const_cast<ProtocolDecl *>(otherProto)},
265-
std::move(temp));
315+
if (ctx.LangOpts.RequirementMachineProtocolSignatures ==
316+
RequirementMachineMode::Enabled) {
317+
SmallVector<RequirementError, 4> errors;
318+
machine->System.computeRedundantRequirementDiagnostics(errors);
319+
diagnoseRequirementErrors(ctx, errors,
320+
/*allowConcreteGenericParams=*/false);
266321
}
267-
}
268322

269-
if (ctx.LangOpts.RequirementMachineProtocolSignatures ==
270-
RequirementMachineMode::Enabled) {
271-
SmallVector<RequirementError, 4> errors;
272-
machine->System.computeRedundantRequirementDiagnostics(errors);
273-
diagnoseRequirementErrors(ctx, errors,
274-
/*allowConcreteGenericParams=*/false);
323+
// Return the result for the specific protocol this request was kicked off on.
324+
return *result;
275325
}
276-
277-
// Return the result for the specific protocol this request was kicked off on.
278-
return *result;
279326
}
280327

281328
/// Builds the top-level generic signature requirements for this rewrite system.
@@ -479,9 +526,12 @@ AbstractGenericSignatureRequestRQM::evaluate(
479526

480527
if (!errorFlags) {
481528
if (shouldSplitConcreteEquivalenceClasses(result.getRequirements(),
529+
/*proto=*/nullptr,
482530
machine.get())) {
531+
++attempt;
483532
splitConcreteEquivalenceClasses(ctx, result.getRequirements(),
484-
machine.get(), result.getGenericParams(),
533+
/*proto=*/nullptr, machine.get(),
534+
result.getGenericParams(),
485535
requirements, attempt);
486536
continue;
487537
}
@@ -646,9 +696,12 @@ InferredGenericSignatureRequestRQM::evaluate(
646696
if (!errorFlags) {
647697
// Check if we need to rebuild the signature.
648698
if (shouldSplitConcreteEquivalenceClasses(result.getRequirements(),
699+
/*proto=*/nullptr,
649700
machine.get())) {
701+
++attempt;
650702
splitConcreteEquivalenceClasses(ctx, result.getRequirements(),
651-
machine.get(), result.getGenericParams(),
703+
/*proto=*/nullptr, machine.get(),
704+
result.getGenericParams(),
652705
requirements, attempt);
653706
continue;
654707
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: not %target-swift-frontend -typecheck %s -debug-generic-signatures 2>&1 | %FileCheck %s
2+
3+
// CHECK-LABEL: .P1@
4+
// CHECK-NEXT: Requirement signature: <Self where Self.[P1]T : P1, Self.[P1]U == Int, Self.[P1]V == Int>
5+
protocol P1 {
6+
associatedtype T : P1
7+
associatedtype U where U == Int
8+
associatedtype V where V == T.U
9+
}
10+
11+
struct G<X> {}
12+
13+
// CHECK-LABEL: .P2@
14+
// CHECK-NEXT: Requirement signature: <Self where Self.[P2]T : P2, Self.[P2]U == G<Self.[P2]X>, Self.[P2]V == G<Self.[P2]T.[P2]X>>
15+
protocol P2 {
16+
associatedtype T : P2
17+
associatedtype U where U == G<X>
18+
associatedtype V where V == T.U
19+
associatedtype X
20+
}
21+
22+
// CHECK-LABEL: .P3@
23+
// CHECK-NEXT: Requirement signature: <Self where Self.[P3]T : P3, Self.[P3]U == G<Self.[P3]X>, Self.[P3]V == G<Self.[P3]X>, Self.[P3]X == Self.[P3]T.[P3]X>
24+
protocol P3 {
25+
associatedtype T : P3
26+
associatedtype U where U == G<X>
27+
associatedtype V where V == T.U, V == G<X>
28+
associatedtype X
29+
}
30+
31+
// CHECK-LABEL: .P4@
32+
// CHECK-NEXT: Requirement signature: <Self where Self.[P4]T : P4, Self.[P4]U == G<Self.[P4]X>, Self.[P4]V == G<Self.[P4]X>, Self.[P4]X == Self.[P4]T.[P4]X>
33+
protocol P4 {
34+
associatedtype T : P4
35+
associatedtype U where U == G<X>
36+
associatedtype V where V == T.U
37+
associatedtype X where X == T.X
38+
}
39+
40+
// We don't split concrete equivalence classes if the signature had an error,
41+
// but we also shouldn't crash in verify() because of an unsplit concrete
42+
// equivalence class.
43+
44+
// CHECK-LABEL: .P4Bad@
45+
// CHECK-NEXT: Requirement signature: <Self where Self.[P4Bad]T : P4Bad, Self.[P4Bad]U == G<Self.[P4Bad]X>, Self.[P4Bad]V == Self.[P4Bad]T.[P4Bad]U>
46+
protocol P4Bad {
47+
associatedtype T : P4Bad
48+
associatedtype U where U == G<X>
49+
associatedtype V where V == T.U
50+
associatedtype X where X == U.X
51+
}

test/decl/protocol/existential_member_accesses_self_assoctype.swift

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -disable-availability-checking -requirement-machine-protocol-signatures=off -requirement-machine-abstract-signatures=on
2-
3-
// TODO: Get this to pass with -requirement-machine-protocol-signatures=on.
1+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -requirement-machine-abstract-signatures=on
42

53
//===----------------------------------------------------------------------===//
64
// Use of protocols with Self or associated type requirements

test/decl/protocol/req/associated_type_inference_fixed_type.swift

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=off
2-
// RUN: not %target-swift-frontend -typecheck -dump-type-witness-systems %s -requirement-machine-protocol-signatures=off 2>&1 | %FileCheck %s
3-
4-
// TODO: Get this to pass with -requirement-machine-protocol-signatures=on.
1+
// RUN: %target-typecheck-verify-swift
2+
// RUN: not %target-swift-frontend -typecheck -dump-type-witness-systems %s 2>&1 | %FileCheck %s
53

64
protocol P1 where A == Never {
75
associatedtype A
@@ -572,7 +570,7 @@ protocol P32e where A == B {
572570
}
573571
protocol Q32: P32e, P32a, P32b, P32c, P32d {}
574572
// expected-error@-1 {{'Self.B' cannot be equal to both 'Never' and 'Int'}}
575-
// expected-error@-2 {{'Self.B' cannot be equal to both 'Void' and 'Int'}}
573+
// expected-error@-2 {{'Self.B' cannot be equal to both '()' and 'Int'}}
576574
// expected-error@-3 {{'Self.A' cannot be equal to both 'Bool' and 'Int'}}
577575
// expected-note@-4 3 {{same-type constraint 'Self.A' == 'Int' implied here}}
578576
do {

0 commit comments

Comments
 (0)