Skip to content

Commit fce1f2c

Browse files
committed
GSB: getConformanceAccessPath() doesn't need to use getMinimalConformanceSource()
Previously we would look for a derived source before an explicit one, on account of the explicit one possibly being redundant. However, the presence of 'self-derived' sources meant that we had to call getMinimalConformanceSource() to ensure the derived sources were actually usable and would not produce an infinite conformance access path. I'd like to remove getMinimalConformanceSource() now that we have an alternate algorithm to identify redundant explicit requirements. Instead, we can handle the explicit case first, by checking for a conformance requirement in the generic signature -- its presence means it was not redundant, by construction. Then once we handle that case, we know we're going to use a derived source, and finding the shortest one seems to be good enough. This fixes the IRGen crash in https://bugs.swift.org/browse/SR-11153; the requirement signatures in that test still have unnecessary same-type requirements printed, so I added a separate RUN: line for those, and it's marked as known-failing with 'not %FileCheck'.
1 parent 566abca commit fce1f2c

File tree

2 files changed

+49
-41
lines changed

2 files changed

+49
-41
lines changed

lib/AST/GenericSignature.cpp

Lines changed: 27 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -776,9 +776,21 @@ GenericSignatureImpl::getConformanceAccessPath(Type type,
776776
auto conforms = equivClass->conformsTo.find(protocol);
777777
assert(conforms != equivClass->conformsTo.end());
778778

779-
// Look at every requirement source for this conformance. If all sources are
780-
// explicit, leave these three values empty. Otherwise, they are computed
781-
// from the 'best' derived requirement source for this conformance.
779+
auto rootType = equivClass->getAnchor(builder, { });
780+
if (hasConformanceInSignature(getRequirements(), rootType, protocol)) {
781+
ConformanceAccessPath::Entry root(rootType, protocol);
782+
ArrayRef<ConformanceAccessPath::Entry> path(root);
783+
784+
ConformanceAccessPath result(builder.getASTContext().AllocateCopy(path));
785+
equivClass->conformanceAccessPathCache.insert({protocol, result});
786+
787+
return result;
788+
}
789+
790+
// This conformance comes from a derived source.
791+
//
792+
// To recover this the conformance, we recursively recover the conformance
793+
// of the shortest parent type to the parent protocol first.
782794
Type shortestParentType;
783795
Type shortestSubjectType;
784796
ProtocolDecl *shortestParentProto = nullptr;
@@ -823,28 +835,13 @@ GenericSignatureImpl::getConformanceAccessPath(Type type,
823835

824836
case RequirementSource::ProtocolRequirement:
825837
case RequirementSource::InferredProtocolRequirement: {
826-
if (source->parent->kind == RequirementSource::RequirementSignatureSelf) {
827-
// This is a top-level requirement in the requirement signature that is
828-
// currently being computed. This is not a derived source, so it
829-
// contributes nothing to the "shortest parent type" computation.
830-
break;
831-
}
832-
833-
auto constraintType = constraint.getSubjectDependentType({ });
834-
835-
// Skip self-recursive sources.
836-
bool derivedViaConcrete = false;
837-
if (source->getMinimalConformanceSource(builder, constraintType, protocol,
838-
derivedViaConcrete) != source)
839-
break;
840-
838+
assert(source->parent->kind != RequirementSource::RequirementSignatureSelf);
841839

842840
// If we have a derived conformance requirement like T[.P].X : Q, we can
843841
// recursively compute the conformance access path for T : P, and append
844842
// the path element (Self.X : Q).
845843
auto parentType = source->parent->getAffectedType()->getCanonicalType();
846844
auto subjectType = source->getStoredType()->getCanonicalType();
847-
848845
auto *parentProto = source->getProtocolDecl();
849846

850847
// We might have multiple candidate parent types and protocols for the
@@ -861,33 +858,22 @@ GenericSignatureImpl::getConformanceAccessPath(Type type,
861858
}
862859
}
863860

861+
assert(shortestParentType);
862+
864863
SmallVector<ConformanceAccessPath::Entry, 2> path;
865864

866-
if (!shortestParentType) {
867-
// All requirement sources were explicit. This means we can recover the
868-
// conformance directly from the generic signature; canonicalize the
869-
// dependent type and add it as an initial path element.
870-
auto rootType = equivClass->getAnchor(builder, { });
871-
assert(hasConformanceInSignature(getRequirements(), rootType, protocol));
872-
path.emplace_back(rootType, protocol);
873-
} else {
874-
// This conformance comes from a derived source.
875-
//
876-
// To recover this the conformance, we recursively recover the conformance
877-
// of the parent type to the parent protocol first.
878-
auto parentPath = getConformanceAccessPath(
879-
shortestParentType, shortestParentProto);
880-
for (auto entry : parentPath)
881-
path.push_back(entry);
882-
883-
// Then, we add the subject type from the parent protocol's requirement
884-
// signature.
885-
path.emplace_back(shortestSubjectType, protocol);
886-
}
865+
auto parentPath = getConformanceAccessPath(
866+
shortestParentType, shortestParentProto);
867+
for (auto entry : parentPath)
868+
path.push_back(entry);
887869

888-
ConformanceAccessPath result(getASTContext().AllocateCopy(path));
870+
// Then, we add the subject type from the parent protocol's requirement
871+
// signature.
872+
path.emplace_back(shortestSubjectType, protocol);
889873

874+
ConformanceAccessPath result(builder.getASTContext().AllocateCopy(path));
890875
equivClass->conformanceAccessPathCache.insert({protocol, result});
876+
891877
return result;
892878
}
893879

test/Generics/sr11153.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | not %FileCheck %s
2+
// RUN: %target-swift-frontend -emit-ir %s
3+
4+
// CHECK: Requirement signature: <Self where Self.Field : FieldAlgebra>
5+
public protocol VectorSpace {
6+
associatedtype Field: FieldAlgebra
7+
}
8+
9+
// CHECK: Requirement signature: <Self where Self : VectorSpace, Self == Self.Field, Self.ComparableSubalgebra : ComparableFieldAlgebra>
10+
public protocol FieldAlgebra: VectorSpace where Self.Field == Self {
11+
associatedtype ComparableSubalgebra: ComparableFieldAlgebra
12+
static var zero: Self { get }
13+
}
14+
15+
// CHECK: Requirement signature: <Self where Self : FieldAlgebra, Self == Self.ComparableSubalgebra>
16+
public protocol ComparableFieldAlgebra: FieldAlgebra where Self.ComparableSubalgebra == Self {
17+
}
18+
19+
// CHECK: Generic signature: <F where F : FieldAlgebra>
20+
public func test<F: FieldAlgebra>(_ f: F) {
21+
_ = F.ComparableSubalgebra.zero
22+
}

0 commit comments

Comments
 (0)