Skip to content

Commit 88c26e4

Browse files
committed
[GSB] Canonicalize conformance access paths on-the-fly.
When forming a conformance access path, remove from consideration any requirement sources that contain protocol requirements that aren't found in the requirement signature. This ensure well-formedness of the resulting conformance access path. Huge thanks to Slava for reducing this one, and apologies to Arnold for having to track it down a second time before I fixed it. Fixes SR-6200 / rdar://problem/35113583. (cherry picked from commit a77dec1)
1 parent 82e08e0 commit 88c26e4

File tree

2 files changed

+95
-31
lines changed

2 files changed

+95
-31
lines changed

lib/AST/GenericSignature.cpp

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -900,12 +900,70 @@ namespace {
900900
using GSBConstraint = GenericSignatureBuilder::Constraint<T>;
901901
} // end anonymous namespace
902902

903+
/// Determine whether there is a conformance of the given
904+
/// subject type to the given protocol within the given set of explicit
905+
/// requirements.
906+
static bool hasConformanceInSignature(ArrayRef<Requirement> requirements,
907+
Type subjectType,
908+
ProtocolDecl *proto) {
909+
// Make sure this requirement exists in the requirement signature.
910+
for (const auto& req: requirements) {
911+
if (req.getKind() == RequirementKind::Conformance &&
912+
req.getFirstType()->isEqual(subjectType) &&
913+
req.getSecondType()->castTo<ProtocolType>()->getDecl()
914+
== proto) {
915+
return true;
916+
}
917+
}
918+
919+
return false;
920+
}
921+
922+
/// Check whether the given requirement source has any non-canonical protocol
923+
/// requirements in it.
924+
static bool hasNonCanonicalSelfProtocolRequirement(
925+
const RequirementSource *source,
926+
ProtocolDecl *conformingProto) {
927+
for (; source; source = source->parent) {
928+
// Only look at protocol requirements.
929+
if (!source->isProtocolRequirement())
930+
continue;
931+
932+
// If we don't already have a requirement signature for this protocol,
933+
// build one now.
934+
auto inProto = source->getProtocolDecl();
935+
if (!inProto->isRequirementSignatureComputed()) {
936+
inProto->computeRequirementSignature();
937+
assert(inProto->isRequirementSignatureComputed() &&
938+
"couldn't compute requirement signature?");
939+
}
940+
941+
// Check whether the given requirement is in the requirement signature.
942+
if (!hasConformanceInSignature(inProto->getRequirementSignature(),
943+
source->getStoredType(), conformingProto))
944+
return true;
945+
946+
// Update the conforming protocol for the rest of the search.
947+
conformingProto = inProto;
948+
}
949+
950+
return false;
951+
}
952+
903953
/// Retrieve the best requirement source from the list
904954
static const RequirementSource *
905-
getBestRequirementSource(ArrayRef<GSBConstraint<ProtocolDecl *>> constraints) {
955+
getBestCanonicalRequirementSource(
956+
ArrayRef<GSBConstraint<ProtocolDecl *>> constraints) {
906957
const RequirementSource *bestSource = nullptr;
907958
for (const auto &constraint : constraints) {
908959
auto source = constraint.source;
960+
961+
// If there is a non-canonical protocol requirement next to the root,
962+
// skip this requirement source.
963+
if (hasNonCanonicalSelfProtocolRequirement(source, constraint.value))
964+
continue;
965+
966+
// Check whether this is better than our best source.
909967
if (!bestSource || source->compare(bestSource) < 0)
910968
bestSource = source;
911969
}
@@ -934,27 +992,6 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
934992
typedef GenericSignatureBuilder::RequirementSource RequirementSource;
935993
ConformanceAccessPath path;
936994

937-
#ifndef NDEBUG
938-
// Local function to determine whether there is a conformance of the given
939-
// subject type to the given protocol within the given set of explicit
940-
// requirements.
941-
auto hasConformanceInSignature = [&](ArrayRef<Requirement> requirements,
942-
Type subjectType,
943-
ProtocolDecl *proto) -> bool {
944-
// Make sure this requirement exists in the requirement signature.
945-
for (const auto& req: requirements) {
946-
if (req.getKind() == RequirementKind::Conformance &&
947-
req.getFirstType()->isEqual(subjectType) &&
948-
req.getSecondType()->castTo<ProtocolType>()->getDecl()
949-
== proto) {
950-
return true;
951-
}
952-
}
953-
954-
return false;
955-
};
956-
#endif
957-
958995
// Local function to construct the conformance access path from the
959996
// requirement.
960997
std::function<void(ArrayRef<Requirement>, const RequirementSource *,
@@ -1003,13 +1040,6 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
10031040
return;
10041041
}
10051042

1006-
// Canonicalize this step with respect to the requirement signature.
1007-
if (!inProtocol->isRequirementSignatureComputed()) {
1008-
inProtocol->computeRequirementSignature();
1009-
assert(inProtocol->isRequirementSignatureComputed() &&
1010-
"missing signature");
1011-
}
1012-
10131043
// Get a generic signature for the protocol's signature.
10141044
auto inProtoSig = inProtocol->getGenericSignature();
10151045
auto &inProtoSigBuilder = *inProtoSig->getGenericSignatureBuilder();
@@ -1031,7 +1061,7 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
10311061
assert(conforms != equivClass->conformsTo.end());
10321062

10331063
// Compute the root type, canonicalizing it w.r.t. the protocol context.
1034-
auto conformsSource = getBestRequirementSource(conforms->second);
1064+
auto conformsSource = getBestCanonicalRequirementSource(conforms->second);
10351065
assert(conformsSource != source || !requirementSignatureProto);
10361066
Type localRootType = conformsSource->getRootType();
10371067
localRootType = inProtoSig->getCanonicalTypeInContext(localRootType);
@@ -1079,7 +1109,7 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
10791109
};
10801110

10811111
// Canonicalize the root type.
1082-
auto source = getBestRequirementSource(conforms->second);
1112+
auto source = getBestCanonicalRequirementSource(conforms->second);
10831113
Type rootType = source->getRootType()->getCanonicalType(this);
10841114

10851115
// Build the path.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -primary-file %s -emit-ir > %t.ll
2+
// RUN: %FileCheck %s < %t.ll
3+
4+
5+
// SR-6200: canonicalizing a conformance access path that was built
6+
// without the requirement signature.
7+
public struct Valid<V> {}
8+
9+
extension Valid where V: ValidationSuite {}
10+
11+
public protocol Validatable {}
12+
13+
extension Validatable {
14+
public func tested() {}
15+
16+
// CHECK-LABEL: define{{.*}}_T023conformance_access_path11ValidatablePAAE6testedyqd__m2by_t9InputTypeQyd__RszAA15ValidationSuiteRd__lF
17+
public func tested<S: ValidationSuite>(by suite: S.Type) where S.InputType == Self {
18+
// CHECK: [[S_AS_VALIDATION_SUITE:%[0-9]+]] = load i8*, i8** %S.ValidationSuite
19+
// CHECK-NEXT: [[S_VALIDATOR_BASE:%.*]] = bitcast i8* [[S_AS_VALIDATION_SUITE]] to i8**
20+
// CHECK-NEXT: [[S_VALIDATABLE_ADDR:%[0-9]+]] = getelementptr inbounds i8*, i8** [[S_VALIDATOR_BASE]], i32 1
21+
// CHECK-NEXT: [[S_VALIDATABLE_FN_RAW:%[0-9]+]] = load i8*, i8** [[S_VALIDATABLE_ADDR]]
22+
// CHECK-NEXT: [[S_VALIDATABLE_FN:%[0-9]+]] = bitcast i8* [[S_VALIDATABLE_FN_RAW]] to i8** (%swift.type*, %swift.type*, i8**)*
23+
// CHECK-NEXT: call i8** [[S_VALIDATABLE_FN]](%swift.type* %Self, %swift.type* %S, i8** %S.Validator)
24+
tested()
25+
}
26+
}
27+
28+
public protocol Validator {
29+
associatedtype InputType: Validatable
30+
}
31+
32+
public protocol ValidationSuite: Validator {
33+
associatedtype InputType: Validatable
34+
}

0 commit comments

Comments
 (0)