Skip to content

Commit 17e2d6a

Browse files
committed
RequirementMachine: Avoid term->type->term round-trip in getConformanceAccessPath()
We would skip recording a conformance access path if the subject type canonicalized to a concrete type, but this was incorrect. The correct formulation is to use the _canonical anchor_ and not the canonical type as the caching key; that is, we always want it to be a type parameter, even if it is fixed to a concrete type, because type parameters fixed to concrete types can appear in the middle of conformance access paths, as the example in the radar demonstrates. Fixes rdar://problem/83687967.
1 parent 9fb2897 commit 17e2d6a

File tree

2 files changed

+41
-34
lines changed

2 files changed

+41
-34
lines changed

lib/AST/RequirementMachine/GenericSignatureQueries.cpp

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -434,19 +434,6 @@ Type RequirementMachine::getCanonicalTypeInContext(
434434
});
435435
}
436436

437-
/// Replace 'Self' in the given dependent type (\c depTy) with the given
438-
/// dependent type, producing a type that refers to the nested type.
439-
static Type replaceSelfWithType(Type selfType, Type depTy) {
440-
if (auto depMemTy = depTy->getAs<DependentMemberType>()) {
441-
Type baseType = replaceSelfWithType(selfType, depMemTy->getBase());
442-
assert(depMemTy->getAssocType() && "Missing associated type");
443-
return DependentMemberType::get(baseType, depMemTy->getAssocType());
444-
}
445-
446-
assert(depTy->is<GenericTypeParamType>() && "missing Self?");
447-
return selfType;
448-
}
449-
450437
/// Retrieve the conformance access path used to extract the conformance of
451438
/// interface \c type to the given \c protocol.
452439
///
@@ -462,12 +449,29 @@ static Type replaceSelfWithType(Type selfType, Type depTy) {
462449
ConformanceAccessPath
463450
RequirementMachine::getConformanceAccessPath(Type type,
464451
ProtocolDecl *protocol) {
465-
auto canType = getCanonicalTypeInContext(type, { })->getCanonicalType();
466-
assert(canType->isTypeParameter());
452+
assert(type->isTypeParameter());
453+
454+
auto mutTerm = Context.getMutableTermForType(type->getCanonicalType(),
455+
/*proto=*/nullptr);
456+
System.simplify(mutTerm);
457+
verify(mutTerm);
458+
459+
#ifndef NDEBUG
460+
auto *props = Map.lookUpProperties(mutTerm);
461+
assert(props &&
462+
"Subject type of conformance access path should be known");
463+
assert(!props->isConcreteType() &&
464+
"Concrete types do not have conformance access paths");
465+
auto conformsTo = props->getConformsTo();
466+
assert(std::find(conformsTo.begin(), conformsTo.end(), protocol) &&
467+
"Subject type of conformance access path must conform to protocol");
468+
#endif
469+
470+
auto term = Term::get(mutTerm, Context);
467471

468472
// Check if we've already cached the result before doing anything else.
469473
auto found = ConformanceAccessPaths.find(
470-
std::make_pair(canType, protocol));
474+
std::make_pair(term, protocol));
471475
if (found != ConformanceAccessPaths.end()) {
472476
return found->second;
473477
}
@@ -476,13 +480,13 @@ RequirementMachine::getConformanceAccessPath(Type type,
476480

477481
FrontendStatsTracer tracer(Stats, "get-conformance-access-path");
478482

479-
auto recordPath = [&](CanType type, ProtocolDecl *proto,
483+
auto recordPath = [&](Term term, ProtocolDecl *proto,
480484
ConformanceAccessPath path) {
481485
// Add the path to the buffer.
482-
CurrentConformanceAccessPaths.emplace_back(type, path);
486+
CurrentConformanceAccessPaths.emplace_back(term, path);
483487

484488
// Add the path to the map.
485-
auto key = std::make_pair(type, proto);
489+
auto key = std::make_pair(term, proto);
486490
auto inserted = ConformanceAccessPaths.insert(
487491
std::make_pair(key, path));
488492
assert(inserted.second);
@@ -508,22 +512,27 @@ RequirementMachine::getConformanceAccessPath(Type type,
508512
ArrayRef<ConformanceAccessPath::Entry> path(root);
509513
ConformanceAccessPath result(ctx.AllocateCopy(path));
510514

511-
recordPath(rootType, rootProto, result);
515+
auto mutTerm = Context.getMutableTermForType(rootType, nullptr);
516+
System.simplify(mutTerm);
517+
518+
auto rootTerm = Term::get(mutTerm, Context);
519+
recordPath(rootTerm, rootProto, result);
512520
}
513521
}
514522

515523
// We enumerate conformance access paths in shortlex order until we find the
516524
// path whose corresponding type canonicalizes to the one we are looking for.
517525
while (true) {
518526
auto found = ConformanceAccessPaths.find(
519-
std::make_pair(canType, protocol));
527+
std::make_pair(term, protocol));
520528
if (found != ConformanceAccessPaths.end()) {
521529
return found->second;
522530
}
523531

524532
if (CurrentConformanceAccessPaths.empty()) {
525533
llvm::errs() << "Failed to find conformance access path for ";
526-
llvm::errs() << type << " " << protocol->getName() << ":\n";
534+
llvm::errs() << type << " (" << term << ")" << " : ";
535+
llvm::errs() << protocol->getName() << ":\n";
527536
type.dump(llvm::errs());
528537
llvm::errs() << "\n";
529538
dump(llvm::errs());
@@ -533,7 +542,7 @@ RequirementMachine::getConformanceAccessPath(Type type,
533542
// The buffer consists of all conformance access paths of length N.
534543
// Swap it out with an empty buffer, and fill it with all paths of
535544
// length N+1.
536-
std::vector<std::pair<CanType, ConformanceAccessPath>> oldPaths;
545+
std::vector<std::pair<Term, ConformanceAccessPath>> oldPaths;
537546
std::swap(CurrentConformanceAccessPaths, oldPaths);
538547

539548
for (const auto &pair : oldPaths) {
@@ -551,21 +560,19 @@ RequirementMachine::getConformanceAccessPath(Type type,
551560
auto nextSubjectType = req.getFirstType()->getCanonicalType();
552561
auto *nextProto = req.getProtocolDecl();
553562

554-
// Compute the canonical anchor for this conformance requirement.
555-
auto nextType = replaceSelfWithType(pair.first, nextSubjectType);
556-
auto nextCanType = getCanonicalTypeInContext(nextType, { })
557-
->getCanonicalType();
563+
MutableTerm mutTerm(pair.first);
564+
mutTerm.append(Context.getMutableTermForType(nextSubjectType,
565+
/*proto=*/lastProto));
566+
System.simplify(mutTerm);
558567

559-
// Skip "derived via concrete" sources.
560-
if (!nextCanType->isTypeParameter())
561-
continue;
568+
auto nextTerm = Term::get(mutTerm, Context);
562569

563570
// If we've already seen a path for this conformance, skip it and
564571
// don't add it to the buffer. Note that because we iterate over
565572
// conformance access paths in shortlex order, the existing
566573
// conformance access path is shorter than the one we found just now.
567574
if (ConformanceAccessPaths.count(
568-
std::make_pair(nextCanType, nextProto)))
575+
std::make_pair(nextTerm, nextProto)))
569576
continue;
570577

571578
if (entries.empty()) {
@@ -580,7 +587,7 @@ RequirementMachine::getConformanceAccessPath(Type type,
580587
ConformanceAccessPath result = ctx.AllocateCopy(entries);
581588
entries.pop_back();
582589

583-
recordPath(nextCanType, nextProto, result);
590+
recordPath(nextTerm, nextProto, result);
584591
}
585592
}
586593
}

lib/AST/RequirementMachine/RequirementMachine.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,14 @@ class RequirementMachine final {
6161
UnifiedStatsReporter *Stats;
6262

6363
/// All conformance access paths computed so far.
64-
llvm::DenseMap<std::pair<CanType, ProtocolDecl *>,
64+
llvm::DenseMap<std::pair<Term, ProtocolDecl *>,
6565
ConformanceAccessPath> ConformanceAccessPaths;
6666

6767
/// Conformance access paths computed during the last round. All elements
6868
/// have the same length. If a conformance access path of greater length
6969
/// is requested, we refill CurrentConformanceAccessPaths with all paths of
7070
/// length N+1, and add them to the ConformanceAccessPaths map.
71-
std::vector<std::pair<CanType, ConformanceAccessPath>>
71+
std::vector<std::pair<Term, ConformanceAccessPath>>
7272
CurrentConformanceAccessPaths;
7373

7474
explicit RequirementMachine(RewriteContext &rewriteCtx);

0 commit comments

Comments
 (0)