Skip to content

Commit 2752063

Browse files
author
ematejska
authored
Merge pull request #13428 from DougGregor/canonicalize-conformance-access-path-4.1
[4.1] [GSB] Canonicalize conformance access paths on-the-fly.
2 parents ed95797 + 9b2c185 commit 2752063

File tree

3 files changed

+98
-31
lines changed

3 files changed

+98
-31
lines changed

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,9 +936,11 @@ class GenericSignatureBuilder::RequirementSource final
936936
/// Whether there is a trailing written requirement location.
937937
const bool hasTrailingWrittenRequirementLoc;
938938

939+
public:
939940
/// Whether a protocol requirement came from the requirement signature.
940941
const bool usesRequirementSignature;
941942

943+
private:
942944
/// The actual storage, described by \c storageKind.
943945
union {
944946
/// The type to which a requirement applies.

lib/AST/GenericSignature.cpp

Lines changed: 62 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -900,12 +900,71 @@ 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 (!source->usesRequirementSignature &&
943+
!hasConformanceInSignature(inProto->getRequirementSignature(),
944+
source->getStoredType(), conformingProto))
945+
return true;
946+
947+
// Update the conforming protocol for the rest of the search.
948+
conformingProto = inProto;
949+
}
950+
951+
return false;
952+
}
953+
903954
/// Retrieve the best requirement source from the list
904955
static const RequirementSource *
905-
getBestRequirementSource(ArrayRef<GSBConstraint<ProtocolDecl *>> constraints) {
956+
getBestCanonicalRequirementSource(
957+
ArrayRef<GSBConstraint<ProtocolDecl *>> constraints) {
906958
const RequirementSource *bestSource = nullptr;
907959
for (const auto &constraint : constraints) {
908960
auto source = constraint.source;
961+
962+
// If there is a non-canonical protocol requirement next to the root,
963+
// skip this requirement source.
964+
if (hasNonCanonicalSelfProtocolRequirement(source, constraint.value))
965+
continue;
966+
967+
// Check whether this is better than our best source.
909968
if (!bestSource || source->compare(bestSource) < 0)
910969
bestSource = source;
911970
}
@@ -934,27 +993,6 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
934993
typedef GenericSignatureBuilder::RequirementSource RequirementSource;
935994
ConformanceAccessPath path;
936995

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-
958996
// Local function to construct the conformance access path from the
959997
// requirement.
960998
std::function<void(ArrayRef<Requirement>, const RequirementSource *,
@@ -1003,13 +1041,6 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
10031041
return;
10041042
}
10051043

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-
10131044
// Get a generic signature for the protocol's signature.
10141045
auto inProtoSig = inProtocol->getGenericSignature();
10151046
auto &inProtoSigBuilder = *inProtoSig->getGenericSignatureBuilder();
@@ -1031,7 +1062,7 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
10311062
assert(conforms != equivClass->conformsTo.end());
10321063

10331064
// Compute the root type, canonicalizing it w.r.t. the protocol context.
1034-
auto conformsSource = getBestRequirementSource(conforms->second);
1065+
auto conformsSource = getBestCanonicalRequirementSource(conforms->second);
10351066
assert(conformsSource != source || !requirementSignatureProto);
10361067
Type localRootType = conformsSource->getRootType();
10371068
localRootType = inProtoSig->getCanonicalTypeInContext(localRootType);
@@ -1079,7 +1110,7 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
10791110
};
10801111

10811112
// Canonicalize the root type.
1082-
auto source = getBestRequirementSource(conforms->second);
1113+
auto source = getBestCanonicalRequirementSource(conforms->second);
10831114
Type rootType = source->getRootType()->getCanonicalType(this);
10841115

10851116
// 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)