Skip to content

Commit ddc8208

Browse files
authored
Merge pull request #20810 from xedin/rdar-45957015-5.0
[5.0][GSB] When adding same-type requirements pick representative based ...
2 parents 41823e6 + 0b74224 commit ddc8208

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"
@@ -4889,12 +4890,8 @@ GenericSignatureBuilder::addSameTypeRequirementBetweenTypeParameters(
48894890
auto T1 = OrigT1->getRepresentative();
48904891
auto T2 = OrigT2->getRepresentative();
48914892

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

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

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

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

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

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)