Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 27 additions & 41 deletions lib/AST/GenericSignature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -776,9 +776,21 @@ GenericSignatureImpl::getConformanceAccessPath(Type type,
auto conforms = equivClass->conformsTo.find(protocol);
assert(conforms != equivClass->conformsTo.end());

// Look at every requirement source for this conformance. If all sources are
// explicit, leave these three values empty. Otherwise, they are computed
// from the 'best' derived requirement source for this conformance.
auto rootType = equivClass->getAnchor(builder, { });
if (hasConformanceInSignature(getRequirements(), rootType, protocol)) {
ConformanceAccessPath::Entry root(rootType, protocol);
ArrayRef<ConformanceAccessPath::Entry> path(root);

ConformanceAccessPath result(builder.getASTContext().AllocateCopy(path));
equivClass->conformanceAccessPathCache.insert({protocol, result});

return result;
}

// This conformance comes from a derived source.
//
// To recover this the conformance, we recursively recover the conformance
// of the shortest parent type to the parent protocol first.
Type shortestParentType;
Type shortestSubjectType;
ProtocolDecl *shortestParentProto = nullptr;
Expand Down Expand Up @@ -823,28 +835,13 @@ GenericSignatureImpl::getConformanceAccessPath(Type type,

case RequirementSource::ProtocolRequirement:
case RequirementSource::InferredProtocolRequirement: {
if (source->parent->kind == RequirementSource::RequirementSignatureSelf) {
// This is a top-level requirement in the requirement signature that is
// currently being computed. This is not a derived source, so it
// contributes nothing to the "shortest parent type" computation.
break;
}

auto constraintType = constraint.getSubjectDependentType({ });

// Skip self-recursive sources.
bool derivedViaConcrete = false;
if (source->getMinimalConformanceSource(builder, constraintType, protocol,
derivedViaConcrete) != source)
break;

assert(source->parent->kind != RequirementSource::RequirementSignatureSelf);

// If we have a derived conformance requirement like T[.P].X : Q, we can
// recursively compute the conformance access path for T : P, and append
// the path element (Self.X : Q).
auto parentType = source->parent->getAffectedType()->getCanonicalType();
auto subjectType = source->getStoredType()->getCanonicalType();

auto *parentProto = source->getProtocolDecl();

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

assert(shortestParentType);

SmallVector<ConformanceAccessPath::Entry, 2> path;

if (!shortestParentType) {
// All requirement sources were explicit. This means we can recover the
// conformance directly from the generic signature; canonicalize the
// dependent type and add it as an initial path element.
auto rootType = equivClass->getAnchor(builder, { });
assert(hasConformanceInSignature(getRequirements(), rootType, protocol));
path.emplace_back(rootType, protocol);
} else {
// This conformance comes from a derived source.
//
// To recover this the conformance, we recursively recover the conformance
// of the parent type to the parent protocol first.
auto parentPath = getConformanceAccessPath(
shortestParentType, shortestParentProto);
for (auto entry : parentPath)
path.push_back(entry);

// Then, we add the subject type from the parent protocol's requirement
// signature.
path.emplace_back(shortestSubjectType, protocol);
}
auto parentPath = getConformanceAccessPath(
shortestParentType, shortestParentProto);
for (auto entry : parentPath)
path.push_back(entry);

ConformanceAccessPath result(getASTContext().AllocateCopy(path));
// Then, we add the subject type from the parent protocol's requirement
// signature.
path.emplace_back(shortestSubjectType, protocol);

ConformanceAccessPath result(builder.getASTContext().AllocateCopy(path));
equivClass->conformanceAccessPathCache.insert({protocol, result});

return result;
}

Expand Down
22 changes: 22 additions & 0 deletions test/Generics/sr11153.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | not %FileCheck %s
// RUN: %target-swift-frontend -emit-ir %s

// CHECK: Requirement signature: <Self where Self.Field : FieldAlgebra>
public protocol VectorSpace {
associatedtype Field: FieldAlgebra
}

// CHECK: Requirement signature: <Self where Self : VectorSpace, Self == Self.Field, Self.ComparableSubalgebra : ComparableFieldAlgebra>
public protocol FieldAlgebra: VectorSpace where Self.Field == Self {
associatedtype ComparableSubalgebra: ComparableFieldAlgebra
static var zero: Self { get }
}

// CHECK: Requirement signature: <Self where Self : FieldAlgebra, Self == Self.ComparableSubalgebra>
public protocol ComparableFieldAlgebra: FieldAlgebra where Self.ComparableSubalgebra == Self {
}

// CHECK: Generic signature: <F where F : FieldAlgebra>
public func test<F: FieldAlgebra>(_ f: F) {
_ = F.ComparableSubalgebra.zero
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
protocol Snapshotting {
associatedtype NativeType: NativeInserting where NativeType.SnapshotType == Self
associatedtype ChangeType: SnapshotChange where ChangeType.SnapshotType == Self
}

protocol NativeInserting {
associatedtype SnapshotType : Snapshotting where SnapshotType.NativeType == Self
}

protocol SnapshotProperties : OptionSet where RawValue == Int {
static var all: Self { get }
}

protocol SnapshotChange {
associatedtype SnapshotType : Snapshotting where SnapshotType.ChangeType == Self
associatedtype PropertiesType : SnapshotProperties
}
15 changes: 15 additions & 0 deletions validation-test/compiler_crashers_2_fixed/sr11153_2.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// RUN: %target-swift-frontend -emit-ir -primary-file %S/Inputs/sr11153_2_other.swift -primary-file %s
// RUN: %target-swift-frontend -emit-ir %S/Inputs/sr11153_2_other.swift %s

class WatchRegistry {
func single<S: Snapshotting>(objectType: S.Type) throws -> Watch<S>
{
return try Watch<S>.singleObject(objectType: S.self, properties: S.ChangeType.PropertiesType.all)
}
}

class Watch<SnapshotType : Snapshotting> {
static func singleObject(objectType: SnapshotType.Type, properties: SnapshotType.ChangeType.PropertiesType) throws -> Watch<SnapshotType> {
fatalError()
}
}