Skip to content

Commit d2d8f70

Browse files
authored
Merge pull request #20646 from xedin/rdar-45957015
[GSB] When adding same-type requirements pick representative based on…
2 parents 30d9e0e + 439784d commit d2d8f70

File tree

4 files changed

+135
-35
lines changed

4 files changed

+135
-35
lines changed

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 113 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "swift/Basic/Defer.h"
3535
#include "swift/Basic/Statistic.h"
3636
#include "llvm/ADT/GraphTraits.h"
37+
#include "llvm/ADT/DenseMap.h"
3738
#include "llvm/ADT/SetVector.h"
3839
#include "llvm/ADT/SmallPtrSet.h"
3940
#include "llvm/ADT/SmallString.h"
@@ -4887,12 +4888,8 @@ GenericSignatureBuilder::addSameTypeRequirementBetweenTypeParameters(
48874888
auto T1 = OrigT1->getRepresentative();
48884889
auto T2 = OrigT2->getRepresentative();
48894890

4890-
// Decide which potential archetype is to be considered the representative.
4891-
// We prefer potential archetypes with lower nesting depths, because it
4892-
// prevents us from unnecessarily building deeply nested potential archetypes.
4893-
unsigned nestingDepth1 = T1->getNestingDepth();
4894-
unsigned nestingDepth2 = T2->getNestingDepth();
4895-
if (nestingDepth2 < nestingDepth1) {
4891+
// Pick representative based on the canonical ordering of the type parameters.
4892+
if (compareDependentTypes(depType2, depType1) < 0) {
48964893
std::swap(T1, T2);
48974894
std::swap(OrigT1, OrigT2);
48984895
std::swap(equivClass, equivClass2);
@@ -4904,6 +4901,8 @@ GenericSignatureBuilder::addSameTypeRequirementBetweenTypeParameters(
49044901
equivClass = T1->getOrCreateEquivalenceClass(*this);
49054902

49064903
// Record this same-type constraint.
4904+
// Let's keep type order in the new constraint the same as it's written
4905+
// in source, which makes it much easier to diagnose later.
49074906
equivClass->sameTypeConstraints.push_back({depType1, depType2, source});
49084907

49094908
// Determine the anchor types of the two equivalence classes.
@@ -6906,34 +6905,118 @@ void GenericSignatureBuilder::checkSameTypeConstraints(
69066905
// connect all of the components, or else we wouldn't have an equivalence
69076906
// class.
69086907
if (intercomponentEdges.size() > numComponents - 1) {
6909-
std::vector<bool> connected(numComponents, false);
6910-
const auto &firstEdge = intercomponentEdges.front();
6911-
for (const auto &edge : intercomponentEdges) {
6912-
// If both the source and target are already connected, this edge is
6913-
// not part of the spanning tree.
6914-
if (connected[edge.source] && connected[edge.target]) {
6915-
if (edge.constraint.source->shouldDiagnoseRedundancy(true) &&
6916-
firstEdge.constraint.source->shouldDiagnoseRedundancy(false)) {
6917-
Diags.diagnose(edge.constraint.source->getLoc(),
6918-
diag::redundant_same_type_constraint,
6919-
edge.constraint.getSubjectDependentType(
6920-
genericParams),
6921-
edge.constraint.value);
6922-
6923-
Diags.diagnose(firstEdge.constraint.source->getLoc(),
6924-
diag::previous_same_type_constraint,
6925-
firstEdge.constraint.source->classifyDiagKind(),
6926-
firstEdge.constraint.getSubjectDependentType(
6927-
genericParams),
6928-
firstEdge.constraint.value);
6908+
// First let's order all of the intercomponent edges
6909+
// as written in source, this helps us to diagnose
6910+
// all of the duplicate constraints in correct order.
6911+
std::vector<IntercomponentEdge *> sourceOrderedEdges;
6912+
for (auto &edge : intercomponentEdges)
6913+
sourceOrderedEdges.push_back(&edge);
6914+
6915+
llvm::array_pod_sort(
6916+
sourceOrderedEdges.begin(), sourceOrderedEdges.end(),
6917+
[](IntercomponentEdge *const *a, IntercomponentEdge *const *b) -> int {
6918+
auto &sourceMgr = (*a)->constraint.value->getASTContext().SourceMgr;
6919+
6920+
auto locA = (*a)->constraint.source->getLoc();
6921+
auto locB = (*b)->constraint.source->getLoc();
6922+
6923+
auto bufferA = sourceMgr.findBufferContainingLoc(locA);
6924+
auto bufferB = sourceMgr.findBufferContainingLoc(locB);
6925+
6926+
if (bufferA != bufferB)
6927+
return bufferA < bufferB ? -1 : 1;
6928+
6929+
auto offsetA = sourceMgr.getLocOffsetInBuffer(locA, bufferA);
6930+
auto offsetB = sourceMgr.getLocOffsetInBuffer(locB, bufferB);
6931+
6932+
return offsetA < offsetB ? -1 : (offsetA == offsetB ? 0 : 1);
6933+
});
6934+
6935+
auto isDiagnosable = [](const IntercomponentEdge &edge, bool isPrimary) {
6936+
return edge.constraint.source->shouldDiagnoseRedundancy(isPrimary);
6937+
};
6938+
6939+
using EquivClass = llvm::DenseMap<Type, unsigned>;
6940+
llvm::DenseMap<Type, EquivClass> equivalences;
6941+
// The idea here is to form an equivalence class per representative
6942+
// (picked from each edge constraint in type parameter order) and
6943+
// propagate all new equivalent types up the chain until duplicate
6944+
// entry is found, that entry is going to point to previous
6945+
// declaration and is going to mark current edge as a duplicate of
6946+
// such entry.
6947+
for (auto edgeIdx : indices(sourceOrderedEdges)) {
6948+
const auto &edge = *sourceOrderedEdges[edgeIdx];
6949+
6950+
Type lhs = edge.constraint.getSubjectDependentType(genericParams);
6951+
Type rhs = edge.constraint.value;
6952+
6953+
// Make sure that representative for equivalence class is picked
6954+
// in canonical type parameter order.
6955+
if (compareDependentTypes(rhs, lhs) < 0)
6956+
std::swap(lhs, rhs);
6957+
6958+
// Index of the previous declaration of the same-type constraint
6959+
// which current edge might be a duplicate of.
6960+
Optional<unsigned> previousIndex;
6961+
6962+
bool isDuplicate = false;
6963+
auto &representative = equivalences[lhs];
6964+
if (representative.insert({rhs, edgeIdx}).second) {
6965+
// Since this is a new equivalence, and right-hand side might
6966+
// be a representative of some other equivalence class,
6967+
// its existing members have to be merged up.
6968+
auto RHSEquivClass = equivalences.find(rhs);
6969+
if (RHSEquivClass != equivalences.end()) {
6970+
auto &equivClass = RHSEquivClass->getSecond();
6971+
representative.insert(equivClass.begin(), equivClass.end());
69296972
}
69306973

6931-
continue;
6974+
// If left-hand side is involved in any other equivalences
6975+
// let's propagate new information up the chain.
6976+
for (auto &e : equivalences) {
6977+
auto &repr = e.first;
6978+
auto &equivClass = e.second;
6979+
6980+
if (repr->isEqual(lhs) || !equivClass.count(lhs))
6981+
continue;
6982+
6983+
if (!equivClass.insert({rhs, edgeIdx}).second) {
6984+
// Even if "previous" edge is not diagnosable we
6985+
// still need to produce diagnostic about main duplicate.
6986+
isDuplicate = true;
6987+
6988+
auto prevIdx = equivClass[rhs];
6989+
if (!isDiagnosable(intercomponentEdges[prevIdx],
6990+
/*isPrimary=*/false))
6991+
continue;
6992+
6993+
// If there is a diagnosable duplicate equivalence,
6994+
// it means that we've found our previous declaration.
6995+
previousIndex = prevIdx;
6996+
break;
6997+
}
6998+
}
6999+
} else {
7000+
// Looks like this is a situation like T.A == T.B, ..., T.B == T.A
7001+
previousIndex = representative[rhs];
7002+
isDuplicate = true;
69327003
}
69337004

6934-
// Put the source and target into the spanning tree.
6935-
connected[edge.source] = true;
6936-
connected[edge.target] = true;
7005+
if (!isDuplicate || !isDiagnosable(edge, /*isPrimary=*/true))
7006+
continue;
7007+
7008+
Diags.diagnose(edge.constraint.source->getLoc(),
7009+
diag::redundant_same_type_constraint,
7010+
edge.constraint.getSubjectDependentType(genericParams),
7011+
edge.constraint.value);
7012+
7013+
if (previousIndex) {
7014+
auto &prevEquiv = sourceOrderedEdges[*previousIndex]->constraint;
7015+
Diags.diagnose(
7016+
prevEquiv.source->getLoc(), diag::previous_same_type_constraint,
7017+
prevEquiv.source->classifyDiagKind(),
7018+
prevEquiv.getSubjectDependentType(genericParams), prevEquiv.value);
7019+
}
69377020
}
69387021
}
69397022

test/Generics/rdar45957015.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
protocol C {
4+
associatedtype T : Collection where T.Element == Self
5+
}
6+
7+
protocol V : C, RawRepresentable where RawValue == String {}
8+
9+
protocol P {
10+
associatedtype A: V
11+
}
12+
13+
extension P {
14+
func foo<U: Collection>(_ args: U) -> String where U.Element == A {
15+
return args.reduce("", { $1.rawValue }) // Ok
16+
}
17+
}

test/Generics/same_type_constraints.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,9 +353,9 @@ func intercomponentSameComponents<T: P10>(_: T)
353353
func intercomponentMoreThanSpanningTree<T: P10>(_: T)
354354
where T.A == T.B,
355355
T.B == T.C,
356-
T.D == T.E, // expected-note{{previous same-type constraint 'T.D' == 'T.E' written here}}
356+
T.D == T.E, // expected-note {{previous same-type constraint 'T.D' == 'T.E' written here}}
357357
T.D == T.B,
358-
T.E == T.B // expected-warning{{redundant same-type constraint 'T.E' == 'T.B'}}
358+
T.E == T.B // expected-warning {{redundant same-type constraint 'T.E' == 'T.B'}}
359359
{ }
360360

361361
func trivialRedundancy<T: P10>(_: T) where T.A == T.A { } // expected-warning{{redundant same-type constraint 'T.A' == 'T.A'}}

test/decl/protocol/req/associated_type_ambiguity.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ protocol P1 {
1515
}
1616

1717
protocol P2 {
18-
associatedtype T // expected-note {{found this candidate}}
18+
associatedtype T // expected-note 2 {{found this candidate}}
1919
}
2020

2121
// FIXME: This extension's generic signature is still minimized differently from
@@ -36,7 +36,7 @@ extension P1 where Self : P2 {
3636
// Same as above, but now we have two visible associated types with the same
3737
// name.
3838
protocol P3 {
39-
associatedtype T
39+
associatedtype T // expected-note {{found this candidate}}
4040
}
4141

4242
// FIXME: This extension's generic signature is still minimized differently from
@@ -48,7 +48,7 @@ extension P2 where Self : P3, T == Int {
4848
}
4949

5050
extension P2 where Self : P3 {
51-
func takeT1(_: T) {}
51+
func takeT1(_: T) {} // expected-error {{'T' is ambiguous for type lookup in this context}}
5252
func takeT2(_: Self.T) {}
5353
}
5454

0 commit comments

Comments
 (0)