Skip to content

Commit 4069434

Browse files
committed
RequirementMachine: Preserve sugar when splitting concrete equivalence classes
Instead of kicking off an AbstractGenericSignatureRequest recursively, handle the rebuilding in a loop in {Abstract,Inferred}GenericSignatureRequest. This also avoids an unnecessary call to verify() when rebuilding.
1 parent b373bd6 commit 4069434

File tree

2 files changed

+102
-79
lines changed

2 files changed

+102
-79
lines changed

lib/AST/RequirementMachine/RequirementMachineRequests.cpp

Lines changed: 88 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ static bool shouldSplitConcreteEquivalenceClass(Requirement req,
254254
sig->isConcreteType(req.getSecondType()));
255255
}
256256

257+
/// Returns true if this generic signature contains abstract same-type
258+
/// requirements between concrete type parameters. In this case, we split
259+
/// the abstract same-type requirements into pairs of concrete type
260+
/// requirements, and minimize the signature again.
257261
static bool shouldSplitConcreteEquivalenceClasses(GenericSignature sig) {
258262
for (auto req : sig.getRequirements()) {
259263
if (shouldSplitConcreteEquivalenceClass(req, sig))
@@ -263,37 +267,34 @@ static bool shouldSplitConcreteEquivalenceClasses(GenericSignature sig) {
263267
return false;
264268
}
265269

266-
static GenericSignature splitConcreteEquivalenceClasses(
267-
GenericSignature sig, ASTContext &ctx) {
268-
SmallVector<Requirement, 2> reqs;
270+
/// Replace each same-type requirement 'T == U' where 'T' (and therefore 'U')
271+
/// is known to equal a concrete type 'C' with a pair of requirements
272+
/// 'T == C' and 'U == C'. We build the signature again in this case, since
273+
/// one of the two requirements will be redundant, but we don't know which
274+
/// ahead of time.
275+
static void splitConcreteEquivalenceClasses(
276+
ASTContext &ctx,
277+
GenericSignature sig,
278+
SmallVectorImpl<StructuralRequirement> &requirements) {
279+
requirements.clear();
269280

270281
for (auto req : sig.getRequirements()) {
271282
if (shouldSplitConcreteEquivalenceClass(req, sig)) {
272283
auto canType = sig->getSugaredType(
273284
sig.getCanonicalTypeInContext(
274285
req.getSecondType()));
275286

276-
reqs.emplace_back(RequirementKind::SameType,
277-
req.getFirstType(),
278-
canType);
279-
reqs.emplace_back(RequirementKind::SameType,
280-
req.getSecondType(),
281-
canType);
282-
} else {
283-
reqs.push_back(req);
287+
Requirement firstReq(RequirementKind::SameType,
288+
req.getFirstType(), canType);
289+
Requirement secondReq(RequirementKind::SameType,
290+
req.getSecondType(), canType);
291+
requirements.push_back({firstReq, SourceLoc(), /*inferred=*/false});
292+
requirements.push_back({secondReq, SourceLoc(), /*inferred=*/false});
293+
continue;
284294
}
285-
}
286-
287-
SmallVector<GenericTypeParamType *, 2> genericParams;
288-
genericParams.append(sig.getGenericParams().begin(),
289-
sig.getGenericParams().end());
290295

291-
return evaluateOrDefault(
292-
ctx.evaluator,
293-
AbstractGenericSignatureRequestRQM{
294-
/*baseSignature=*/nullptr,
295-
genericParams, reqs},
296-
GenericSignatureWithError()).getPointer();
296+
requirements.push_back({req, SourceLoc(), /*inferred=*/false});
297+
}
297298
}
298299

299300
GenericSignatureWithError
@@ -427,32 +428,36 @@ AbstractGenericSignatureRequestRQM::evaluate(
427428
}
428429
}
429430

430-
// Heap-allocate the requirement machine to save stack space.
431-
std::unique_ptr<RequirementMachine> machine(new RequirementMachine(
432-
ctx.getRewriteContext()));
431+
for (;;) {
432+
// Heap-allocate the requirement machine to save stack space.
433+
std::unique_ptr<RequirementMachine> machine(new RequirementMachine(
434+
ctx.getRewriteContext()));
433435

434-
auto status =
435-
machine->initWithWrittenRequirements(genericParams, requirements);
436-
machine->checkCompletionResult(status.first);
436+
auto status =
437+
machine->initWithWrittenRequirements(genericParams, requirements);
438+
machine->checkCompletionResult(status.first);
437439

438-
// We pass reconstituteSugar=false to ensure that if the original
439-
// requirements were canonical, the final signature remains canonical.
440-
auto minimalRequirements =
441-
machine->computeMinimalGenericSignatureRequirements(
442-
/*reconstituteSugar=*/false);
440+
// We pass reconstituteSugar=false to ensure that if the original
441+
// requirements were canonical, the final signature remains canonical.
442+
auto minimalRequirements =
443+
machine->computeMinimalGenericSignatureRequirements(
444+
/*reconstituteSugar=*/false);
443445

444-
auto result = GenericSignature::get(genericParams, minimalRequirements);
445-
auto errorFlags = machine->getErrors();
446+
auto result = GenericSignature::get(genericParams, minimalRequirements);
447+
auto errorFlags = machine->getErrors();
446448

447-
if (!errorFlags) {
448-
if (shouldSplitConcreteEquivalenceClasses(result))
449-
result = splitConcreteEquivalenceClasses(result, ctx);
449+
if (!errorFlags) {
450+
if (shouldSplitConcreteEquivalenceClasses(result)) {
451+
splitConcreteEquivalenceClasses(ctx, result, requirements);
452+
continue;
453+
}
450454

451-
// Check invariants.
452-
result.verify();
453-
}
455+
// Check invariants.
456+
result.verify();
457+
}
454458

455-
return GenericSignatureWithError(result, errorFlags);
459+
return GenericSignatureWithError(result, errorFlags);
460+
}
456461
}
457462

458463
GenericSignatureWithError
@@ -563,51 +568,55 @@ InferredGenericSignatureRequestRQM::evaluate(
563568
}
564569
}
565570

566-
// Heap-allocate the requirement machine to save stack space.
567-
std::unique_ptr<RequirementMachine> machine(new RequirementMachine(
568-
ctx.getRewriteContext()));
571+
for (;;) {
572+
// Heap-allocate the requirement machine to save stack space.
573+
std::unique_ptr<RequirementMachine> machine(new RequirementMachine(
574+
ctx.getRewriteContext()));
569575

570-
auto status =
571-
machine->initWithWrittenRequirements(genericParams, requirements);
572-
if (status.first != CompletionResult::Success) {
573-
ctx.Diags.diagnose(loc,
574-
diag::requirement_machine_completion_failed,
575-
/*protocol=*/0,
576-
unsigned(status.first));
576+
auto status =
577+
machine->initWithWrittenRequirements(genericParams, requirements);
578+
if (status.first != CompletionResult::Success) {
579+
ctx.Diags.diagnose(loc,
580+
diag::requirement_machine_completion_failed,
581+
/*protocol=*/0,
582+
unsigned(status.first));
577583

578-
auto rule = machine->getRuleAsStringForDiagnostics(status.second);
579-
ctx.Diags.diagnose(loc,
580-
diag::requirement_machine_completion_rule,
581-
rule);
584+
auto rule = machine->getRuleAsStringForDiagnostics(status.second);
585+
ctx.Diags.diagnose(loc,
586+
diag::requirement_machine_completion_rule,
587+
rule);
582588

583-
auto result = GenericSignature::get(genericParams,
584-
parentSig.getRequirements());
585-
return GenericSignatureWithError(
586-
result, GenericSignatureErrorFlags::CompletionFailed);
587-
}
589+
auto result = GenericSignature::get(genericParams,
590+
parentSig.getRequirements());
591+
return GenericSignatureWithError(
592+
result, GenericSignatureErrorFlags::CompletionFailed);
593+
}
588594

589-
auto minimalRequirements =
590-
machine->computeMinimalGenericSignatureRequirements(
591-
/*reconstituteSugar=*/true);
595+
auto minimalRequirements =
596+
machine->computeMinimalGenericSignatureRequirements(
597+
/*reconstituteSugar=*/true);
592598

593-
auto result = GenericSignature::get(genericParams, minimalRequirements);
594-
auto errorFlags = machine->getErrors();
599+
auto result = GenericSignature::get(genericParams, minimalRequirements);
600+
auto errorFlags = machine->getErrors();
595601

596-
if (ctx.LangOpts.RequirementMachineInferredSignatures ==
597-
RequirementMachineMode::Enabled) {
598-
machine->System.computeRedundantRequirementDiagnostics(errors);
599-
diagnoseRequirementErrors(ctx, errors, allowConcreteGenericParams);
600-
}
602+
if (ctx.LangOpts.RequirementMachineInferredSignatures ==
603+
RequirementMachineMode::Enabled) {
604+
machine->System.computeRedundantRequirementDiagnostics(errors);
605+
diagnoseRequirementErrors(ctx, errors, allowConcreteGenericParams);
606+
}
601607

602-
// FIXME: Handle allowConcreteGenericParams
608+
// FIXME: Handle allowConcreteGenericParams
603609

604-
if (!errorFlags) {
605-
if (shouldSplitConcreteEquivalenceClasses(result))
606-
result = splitConcreteEquivalenceClasses(result, ctx);
610+
if (!errorFlags) {
611+
if (shouldSplitConcreteEquivalenceClasses(result)) {
612+
splitConcreteEquivalenceClasses(ctx, result, requirements);
613+
continue;
614+
}
607615

608-
// Check invariants.
609-
result.verify();
610-
}
616+
// Check invariants.
617+
result.verify();
618+
}
611619

612-
return GenericSignatureWithError(result, errorFlags);
620+
return GenericSignatureWithError(result, errorFlags);
621+
}
613622
}

test/Generics/reconstitute_sugar.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,17 @@ extension G where X : C<Dictionary<Y, Z>> {}
4545
// CHECK-LABEL: ExtensionDecl line={{.*}} base=G
4646
// CHECK-NEXT: Generic signature: <X, Y, Z where X : C<()>>
4747
extension G where X : C<()> {}
48+
49+
// Make sure we reconstitute sugar when splitting concrete
50+
// equivalence classes too.
51+
52+
protocol P {
53+
associatedtype T where T == [U]
54+
associatedtype U
55+
}
56+
57+
struct G2<T1 : P, T2 : P> {}
58+
59+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=G2
60+
// CHECK-NEXT: Generic signature: <T1, T2 where T1 : P, T2 : P, T1.[P]U == [Int], T2.[P]U == [Int]>
61+
extension G2 where T2.U == [Int], T1.T == T2.T {}

0 commit comments

Comments
 (0)