Skip to content

Commit 0bc6cfe

Browse files
authored
Merge pull request swiftlang#37296 from slavapestov/cleaner-conformance-access-path
GSB: Rewrite getConformanceAccessPath(), again
2 parents 05e1284 + e6ff771 commit 0bc6cfe

File tree

5 files changed

+165
-149
lines changed

5 files changed

+165
-149
lines changed

include/swift/AST/GenericSignature.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class ConformanceAccessPath {
8080
ConformanceAccessPath(ArrayRef<Entry> path) : path(path) {}
8181

8282
friend class GenericSignatureImpl;
83+
friend class GenericSignatureBuilder;
8384

8485
public:
8586
typedef const Entry *const_iterator;
@@ -88,6 +89,8 @@ class ConformanceAccessPath {
8889
const_iterator begin() const { return path.begin(); }
8990
const_iterator end() const { return path.end(); }
9091

92+
const Entry &back() const { return path.back(); }
93+
9194
void print(raw_ostream &OS) const;
9295

9396
SWIFT_DEBUG_DUMP;

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -277,10 +277,6 @@ class GenericSignatureBuilder {
277277
/// Cached nested-type information, which contains the best declaration
278278
/// for a given name.
279279
llvm::SmallDenseMap<Identifier, CachedNestedType> nestedTypeNameCache;
280-
281-
/// Cached access paths.
282-
llvm::SmallDenseMap<const ProtocolDecl *, ConformanceAccessPath, 8>
283-
conformanceAccessPathCache;
284280
};
285281

286282
friend class RequirementSource;
@@ -786,6 +782,22 @@ class GenericSignatureBuilder {
786782
Type getCanonicalTypeInContext(Type type,
787783
TypeArrayView<GenericTypeParamType> genericParams);
788784

785+
/// Retrieve the conformance access path used to extract the conformance of
786+
/// interface \c type to the given \c protocol.
787+
///
788+
/// \param type The interface type whose conformance access path is to be
789+
/// queried.
790+
/// \param protocol A protocol to which \c type conforms.
791+
///
792+
/// \returns the conformance access path that starts at a requirement of
793+
/// this generic signature and ends at the conformance that makes \c type
794+
/// conform to \c protocol.
795+
///
796+
/// \seealso ConformanceAccessPath
797+
ConformanceAccessPath getConformanceAccessPath(Type type,
798+
ProtocolDecl *protocol,
799+
GenericSignature sig);
800+
789801
/// Verify the correctness of the given generic signature.
790802
///
791803
/// This routine will test that the given generic signature is both minimal

lib/AST/GenericSignature.cpp

Lines changed: 2 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -576,154 +576,11 @@ CanGenericSignature::getGenericParams() const{
576576
return {base, params.size()};
577577
}
578578

579-
namespace {
580-
typedef GenericSignatureBuilder::RequirementSource RequirementSource;
581-
582-
template<typename T>
583-
using GSBConstraint = GenericSignatureBuilder::Constraint<T>;
584-
} // end anonymous namespace
585-
586-
/// Determine whether there is a conformance of the given
587-
/// subject type to the given protocol within the given set of explicit
588-
/// requirements.
589-
static bool hasConformanceInSignature(ArrayRef<Requirement> requirements,
590-
Type subjectType,
591-
ProtocolDecl *proto) {
592-
// Make sure this requirement exists in the requirement signature.
593-
for (const auto &req: requirements) {
594-
if (req.getKind() == RequirementKind::Conformance &&
595-
req.getFirstType()->isEqual(subjectType) &&
596-
req.getProtocolDecl() == proto) {
597-
return true;
598-
}
599-
}
600-
601-
return false;
602-
}
603-
604579
ConformanceAccessPath
605580
GenericSignatureImpl::getConformanceAccessPath(Type type,
606581
ProtocolDecl *protocol) const {
607-
assert(type->isTypeParameter() && "not a type parameter");
608-
609-
// Look up the equivalence class for this type.
610-
auto &builder = *getGenericSignatureBuilder();
611-
auto equivClass =
612-
builder.resolveEquivalenceClass(
613-
type,
614-
ArchetypeResolutionKind::CompleteWellFormed);
615-
616-
assert(!equivClass->concreteType &&
617-
"Concrete types don't have conformance access paths");
618-
619-
auto cached = equivClass->conformanceAccessPathCache.find(protocol);
620-
if (cached != equivClass->conformanceAccessPathCache.end())
621-
return cached->second;
622-
623-
// Dig out the conformance of this type to the given protocol, because we
624-
// want its requirement source.
625-
auto conforms = equivClass->conformsTo.find(protocol);
626-
assert(conforms != equivClass->conformsTo.end());
627-
628-
auto rootType = equivClass->getAnchor(builder, { });
629-
if (hasConformanceInSignature(getRequirements(), rootType, protocol)) {
630-
ConformanceAccessPath::Entry root(rootType, protocol);
631-
ArrayRef<ConformanceAccessPath::Entry> path(root);
632-
633-
ConformanceAccessPath result(builder.getASTContext().AllocateCopy(path));
634-
equivClass->conformanceAccessPathCache.insert({protocol, result});
635-
636-
return result;
637-
}
638-
639-
// This conformance comes from a derived source.
640-
//
641-
// To recover this the conformance, we recursively recover the conformance
642-
// of the shortest parent type to the parent protocol first.
643-
Type shortestParentType;
644-
Type shortestSubjectType;
645-
ProtocolDecl *shortestParentProto = nullptr;
646-
647-
auto isShortestPath = [&](Type parentType,
648-
Type subjectType,
649-
ProtocolDecl *parentProto) -> bool {
650-
if (!shortestParentType)
651-
return true;
652-
653-
int cmpParentTypes = compareDependentTypes(parentType, shortestParentType);
654-
if (cmpParentTypes != 0)
655-
return cmpParentTypes < 0;
656-
657-
int cmpSubjectTypes = compareDependentTypes(subjectType, shortestSubjectType);
658-
if (cmpSubjectTypes != 0)
659-
return cmpSubjectTypes < 0;
660-
661-
int cmpParentProtos = TypeDecl::compare(parentProto, shortestParentProto);
662-
return cmpParentProtos < 0;
663-
};
664-
665-
auto recordShortestParentType = [&](Type parentType,
666-
Type subjectType,
667-
ProtocolDecl *parentProto) {
668-
if (isShortestPath(parentType, subjectType, parentProto)) {
669-
shortestParentType = parentType;
670-
shortestSubjectType = subjectType;
671-
shortestParentProto = parentProto;
672-
}
673-
};
674-
675-
for (auto constraint : conforms->second) {
676-
auto *source = constraint.source;
677-
678-
switch (source->kind) {
679-
case RequirementSource::Explicit:
680-
case RequirementSource::Inferred:
681-
// This is not a derived source, so it contributes nothing to the
682-
// "shortest parent type" computation.
683-
break;
684-
685-
case RequirementSource::ProtocolRequirement:
686-
case RequirementSource::InferredProtocolRequirement: {
687-
assert(source->parent->kind != RequirementSource::RequirementSignatureSelf);
688-
689-
// If we have a derived conformance requirement like T[.P].X : Q, we can
690-
// recursively compute the conformance access path for T : P, and append
691-
// the path element (Self.X : Q).
692-
auto parentType = source->parent->getAffectedType()->getCanonicalType();
693-
auto subjectType = source->getStoredType()->getCanonicalType();
694-
auto *parentProto = source->getProtocolDecl();
695-
696-
// We might have multiple candidate parent types and protocols for the
697-
// recursive step, so pick the shortest one.
698-
recordShortestParentType(parentType, subjectType, parentProto);
699-
700-
break;
701-
}
702-
703-
default:
704-
// There should be no other way of testifying to a conformance on a
705-
// dependent type.
706-
llvm_unreachable("Bad requirement source for conformance on dependent type");
707-
}
708-
}
709-
710-
assert(shortestParentType);
711-
712-
SmallVector<ConformanceAccessPath::Entry, 2> path;
713-
714-
auto parentPath = getConformanceAccessPath(
715-
shortestParentType, shortestParentProto);
716-
for (auto entry : parentPath)
717-
path.push_back(entry);
718-
719-
// Then, we add the subject type from the parent protocol's requirement
720-
// signature.
721-
path.emplace_back(shortestSubjectType, protocol);
722-
723-
ConformanceAccessPath result(builder.getASTContext().AllocateCopy(path));
724-
equivClass->conformanceAccessPathCache.insert({protocol, result});
725-
726-
return result;
582+
return getGenericSignatureBuilder()->getConformanceAccessPath(
583+
type, protocol, this);
727584
}
728585

729586
unsigned GenericParamKey::findIndexIn(

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,12 @@ struct GenericSignatureBuilder::Implementation {
744744

745745
llvm::DenseSet<ExplicitRequirement> ExplicitConformancesImpliedByConcrete;
746746

747+
llvm::DenseMap<std::pair<CanType, ProtocolDecl *>,
748+
ConformanceAccessPath> ConformanceAccessPaths;
749+
750+
std::vector<std::pair<CanType, ConformanceAccessPath>>
751+
CurrentConformanceAccessPaths;
752+
747753
#ifndef NDEBUG
748754
/// Whether we've already computed redundant requiremnts.
749755
bool computedRedundantRequirements = false;
@@ -4125,6 +4131,123 @@ Type GenericSignatureBuilder::getCanonicalTypeInContext(Type type,
41254131
});
41264132
}
41274133

4134+
ConformanceAccessPath
4135+
GenericSignatureBuilder::getConformanceAccessPath(Type type,
4136+
ProtocolDecl *protocol,
4137+
GenericSignature sig) {
4138+
auto canType = getCanonicalTypeInContext(type, { })->getCanonicalType();
4139+
assert(canType->isTypeParameter());
4140+
4141+
// Check if we've already cached the result before doing anything else.
4142+
auto found = Impl->ConformanceAccessPaths.find(
4143+
std::make_pair(canType, protocol));
4144+
if (found != Impl->ConformanceAccessPaths.end()) {
4145+
return found->second;
4146+
}
4147+
4148+
FrontendStatsTracer(Context.Stats, "get-conformance-access-path");
4149+
4150+
// If this is the first time we're asked to look up a conformance access path,
4151+
// visit all of the root conformance requirements in our generic signature and
4152+
// add them to the buffer.
4153+
if (Impl->ConformanceAccessPaths.empty()) {
4154+
for (const auto &req : sig->getRequirements()) {
4155+
// We only care about conformance requirements.
4156+
if (req.getKind() != RequirementKind::Conformance)
4157+
continue;
4158+
4159+
auto rootType = req.getFirstType()->getCanonicalType();
4160+
auto *rootProto = req.getProtocolDecl();
4161+
4162+
ConformanceAccessPath::Entry root(rootType, rootProto);
4163+
ArrayRef<ConformanceAccessPath::Entry> path(root);
4164+
ConformanceAccessPath result(Context.AllocateCopy(path));
4165+
4166+
// Add the path to the buffer.
4167+
Impl->CurrentConformanceAccessPaths.emplace_back(rootType, result);
4168+
4169+
// Add the path to the map.
4170+
auto key = std::make_pair(rootType, rootProto);
4171+
auto inserted = Impl->ConformanceAccessPaths.insert(
4172+
std::make_pair(key, result));
4173+
assert(inserted.second);
4174+
(void) inserted;
4175+
}
4176+
}
4177+
4178+
// We keep going until we find the path we are looking for.
4179+
while (true) {
4180+
auto found = Impl->ConformanceAccessPaths.find(
4181+
std::make_pair(canType, protocol));
4182+
if (found != Impl->ConformanceAccessPaths.end()) {
4183+
return found->second;
4184+
}
4185+
4186+
assert(Impl->CurrentConformanceAccessPaths.size() > 0);
4187+
4188+
// Refill the buffer.
4189+
std::vector<std::pair<CanType, ConformanceAccessPath>> morePaths;
4190+
4191+
// From each path in the buffer, compute all paths of length plus one.
4192+
for (const auto &pair : Impl->CurrentConformanceAccessPaths) {
4193+
const auto &lastElt = pair.second.back();
4194+
auto *lastProto = lastElt.second;
4195+
4196+
// A copy of the current path, populated as needed.
4197+
SmallVector<ConformanceAccessPath::Entry, 4> entries;
4198+
4199+
for (const auto &req : lastProto->getRequirementSignature()) {
4200+
// We only care about conformance requirements.
4201+
if (req.getKind() != RequirementKind::Conformance)
4202+
continue;
4203+
4204+
auto nextSubjectType = req.getFirstType()->getCanonicalType();
4205+
auto *nextProto = req.getProtocolDecl();
4206+
4207+
// Compute the canonical anchor for this conformance requirement.
4208+
auto nextType = replaceSelfWithType(pair.first, nextSubjectType);
4209+
auto nextCanType = getCanonicalTypeInContext(nextType, { })
4210+
->getCanonicalType();
4211+
4212+
// Skip "derived via concrete" sources.
4213+
if (!nextCanType->isTypeParameter())
4214+
continue;
4215+
4216+
// Check if we already have a conformance access path for this anchor.
4217+
auto key = std::make_pair(nextCanType, nextProto);
4218+
4219+
// If we've already seen a path for this conformance, skip it and
4220+
// don't add it to the buffer.
4221+
if (Impl->ConformanceAccessPaths.count(key))
4222+
continue;
4223+
4224+
if (entries.empty()) {
4225+
// Fill our temporary vector.
4226+
entries.insert(entries.begin(),
4227+
pair.second.begin(),
4228+
pair.second.end());
4229+
}
4230+
4231+
// Add the next entry.
4232+
entries.emplace_back(nextSubjectType, nextProto);
4233+
ConformanceAccessPath result = Context.AllocateCopy(entries);
4234+
entries.pop_back();
4235+
4236+
// Add the path to the buffer.
4237+
morePaths.emplace_back(nextCanType, result);
4238+
4239+
// Add the path to the map.
4240+
auto inserted = Impl->ConformanceAccessPaths.insert(
4241+
std::make_pair(key, result));
4242+
assert(inserted.second);
4243+
(void) inserted;
4244+
}
4245+
}
4246+
4247+
std::swap(morePaths, Impl->CurrentConformanceAccessPaths);
4248+
}
4249+
}
4250+
41284251
TypeArrayView<GenericTypeParamType>
41294252
GenericSignatureBuilder::getGenericParams() const {
41304253
return TypeArrayView<GenericTypeParamType>(Impl->GenericParams);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s
2+
// RUN: %target-swift-frontend -emit-ir -verify %s
3+
4+
// Reduced from swift-futures project in the source compatibility suite.
5+
6+
public protocol FutureProtocol: FutureConvertible where FutureType == Self {
7+
associatedtype Output
8+
}
9+
10+
public protocol FutureConvertible {
11+
associatedtype FutureType: FutureProtocol
12+
}
13+
14+
func takesFuture<T : FutureProtocol>(_: T.Type) {}
15+
16+
public struct FutureHolder<T : FutureProtocol> {
17+
// CHECK-LABEL: Generic signature: <T, U where T == U.FutureType, U : FutureConvertible>
18+
init<U : FutureConvertible>(_: U) where U.FutureType == T {
19+
takesFuture(T.self)
20+
}
21+
}

0 commit comments

Comments
 (0)