Skip to content

Commit b43c0c4

Browse files
committed
[GSB/Diagnostics] Improve redundant same-type constraint diagnostics
(cherry picked from commit ab3d1f5)
1 parent 13613c6 commit b43c0c4

File tree

2 files changed

+117
-28
lines changed

2 files changed

+117
-28
lines changed

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 114 additions & 25 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"
@@ -4894,7 +4895,6 @@ GenericSignatureBuilder::addSameTypeRequirementBetweenTypeParameters(
48944895
std::swap(T1, T2);
48954896
std::swap(OrigT1, OrigT2);
48964897
std::swap(equivClass, equivClass2);
4897-
std::swap(depType1, depType2);
48984898
}
48994899

49004900
// T1 must have an equivalence class; create one if we don't already have
@@ -4903,6 +4903,8 @@ GenericSignatureBuilder::addSameTypeRequirementBetweenTypeParameters(
49034903
equivClass = T1->getOrCreateEquivalenceClass(*this);
49044904

49054905
// 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.
49064908
equivClass->sameTypeConstraints.push_back({depType1, depType2, source});
49074909

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

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

6933-
// Put the source and target into the spanning tree.
6934-
connected[edge.source] = true;
6935-
connected[edge.target] = true;
7010+
if (!isDuplicate || !isDiagnosable(edge, /*isPrimary=*/true))
7011+
continue;
7012+
7013+
Diags.diagnose(edge.constraint.source->getLoc(),
7014+
diag::redundant_same_type_constraint,
7015+
edge.constraint.getSubjectDependentType(genericParams),
7016+
edge.constraint.value);
7017+
7018+
if (previousIndex) {
7019+
auto &prevEquiv = intercomponentEdges[*previousIndex].constraint;
7020+
Diags.diagnose(
7021+
prevEquiv.source->getLoc(), diag::previous_same_type_constraint,
7022+
prevEquiv.source->classifyDiagKind(),
7023+
prevEquiv.getSubjectDependentType(genericParams), prevEquiv.value);
7024+
}
69367025
}
69377026
}
69387027

test/Generics/same_type_constraints.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -351,11 +351,11 @@ func intercomponentSameComponents<T: P10>(_: T)
351351
T.B == T.A { } // expected-note{{previous same-type constraint 'T.B' == 'T.A' written here}}
352352

353353
func intercomponentMoreThanSpanningTree<T: P10>(_: T)
354-
where T.A == T.B, // expected-note {{previous same-type constraint 'T.A' == 'T.B' written here}}
354+
where T.A == T.B,
355355
T.B == T.C,
356-
T.D == T.E, // expected-warning {{redundant same-type constraint 'T.D' == 'T.E'}}
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
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'}}

0 commit comments

Comments
 (0)